<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Algorithm on 小窝</title><link>https://blog.areacloser.top/tags/algorithm/</link><description>Recent content in Algorithm on 小窝</description><generator>Hugo -- gohugo.io</generator><language>Asia/Shanghai</language><lastBuildDate>Mon, 13 Jul 2026 16:37:52 +0800</lastBuildDate><atom:link href="https://blog.areacloser.top/tags/algorithm/index.xml" rel="self" type="application/rss+xml"/><item><title>KMP算法讲解</title><link>https://blog.areacloser.top/post/kmp-algorithm/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><guid>https://blog.areacloser.top/post/kmp-algorithm/</guid><description>&lt;img src="https://raw.gitcode.com/areacloser/hugoblogstack-image/raw/main/algorithm.png" alt="Featured image of post KMP算法讲解" /&gt;&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;KMP算法（Knuth-Morris-Pratt）是字符串匹配领域的“灵魂算法”。它的核心智慧在于：&lt;strong&gt;当匹配失败时，利用已经匹配成功的部分信息，让模式串“智能地”向右滑动，而文本指针绝不回溯。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了让你彻底理解，我们从&lt;strong&gt;痛点&lt;/strong&gt;出发，逐步拆解。&lt;/p&gt;
&lt;h2 id="正文"&gt;正文
&lt;/h2&gt;&lt;h3 id="1-暴力匹配的痛点"&gt;1. 暴力匹配的痛点
&lt;/h3&gt;&lt;p&gt;在暴力匹配中，若文本串 &lt;code&gt;S&lt;/code&gt; 为 &lt;code&gt;AAAAAB&lt;/code&gt;，模式串 &lt;code&gt;P&lt;/code&gt; 为 &lt;code&gt;AAAAB&lt;/code&gt;：
当匹配到 &lt;code&gt;S[4]=A&lt;/code&gt; 与 &lt;code&gt;P[4]=B&lt;/code&gt; 失配时，暴力法会把模式串整体右移一位，从 &lt;code&gt;S[1]&lt;/code&gt; 重新开始比较。
这意味着 &lt;strong&gt;&lt;code&gt;i&lt;/code&gt;（文本指针）发生了回溯&lt;/strong&gt;，之前比较过的字符又被重复比较，时间复杂度高达 &lt;strong&gt;O(n*m)&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;KMP的绝招就是：&lt;strong&gt;&lt;code&gt;i&lt;/code&gt; 永远不后退，只让 &lt;code&gt;j&lt;/code&gt;（模式串指针）回退到合适的位置。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-核心武器最长相等前后缀lps--next数组"&gt;2. 核心武器：最长相等前后缀（LPS / Next数组）
&lt;/h3&gt;&lt;p&gt;为了让 &lt;code&gt;j&lt;/code&gt; 知道该回退到哪里，我们需要预处理模式串，生成一个 &lt;strong&gt;Next数组&lt;/strong&gt;（也叫部分匹配表，PMT）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定义&lt;/strong&gt;：&lt;code&gt;next[i]&lt;/code&gt; 表示 &lt;strong&gt;模式串 &lt;code&gt;P[0...i]&lt;/code&gt; 这个子串中，最长的相等真前缀与真后缀的长度&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;真前缀/后缀&lt;/strong&gt;：不包含字符串本身的前缀/后缀。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;举个经典例子&lt;/strong&gt;：模式串 &lt;code&gt;P = &amp;quot;ABABC&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style="text-align: left"&gt;子串&lt;/th&gt;
					&lt;th style="text-align: left"&gt;前缀集合&lt;/th&gt;
					&lt;th style="text-align: left"&gt;后缀集合&lt;/th&gt;
					&lt;th style="text-align: left"&gt;最长相等长度 (&lt;code&gt;next&lt;/code&gt;)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;无&lt;/td&gt;
					&lt;td style="text-align: left"&gt;无&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;AB&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{A}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{B}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;ABA&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{A, AB}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{A, BA}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;strong&gt;1&lt;/strong&gt; (A)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;ABAB&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{A, AB, ABA}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{B, AB, BAB}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;strong&gt;2&lt;/strong&gt; (AB)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;ABABC&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{A, AB, ABA, ABAB}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;code&gt;{C, BC, ABC, BABC}&lt;/code&gt;&lt;/td&gt;
					&lt;td style="text-align: left"&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以 &lt;code&gt;next = [0, 0, 1, 2, 0]&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="3-匹配时的跳跃逻辑"&gt;3. 匹配时的“跳跃”逻辑
&lt;/h3&gt;&lt;p&gt;当我们在文本 &lt;code&gt;S&lt;/code&gt; 中匹配 &lt;code&gt;P&lt;/code&gt; 时，假设匹配到 &lt;code&gt;S[i]&lt;/code&gt; 与 &lt;code&gt;P[j]&lt;/code&gt; 失配了（&lt;code&gt;j&lt;/code&gt; 是当前模式串要比较的位置）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果 &lt;code&gt;j &amp;gt; 0&lt;/code&gt;，说明 &lt;code&gt;P[0...j-1]&lt;/code&gt; 这一段是匹配成功的。&lt;/li&gt;
&lt;li&gt;我们查看 &lt;code&gt;next[j-1]&lt;/code&gt;，设其值为 &lt;code&gt;k&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;这意味着，在已匹配的前缀 &lt;code&gt;P[0...j-1]&lt;/code&gt; 中，&lt;strong&gt;最前面的 &lt;code&gt;k&lt;/code&gt; 个字符&lt;/strong&gt; 和 &lt;strong&gt;最后面的 &lt;code&gt;k&lt;/code&gt; 个字符&lt;/strong&gt; 是一样的。&lt;/li&gt;
&lt;li&gt;因此，我们可以直接把 &lt;code&gt;j&lt;/code&gt; 跳到 &lt;code&gt;k&lt;/code&gt;（即 &lt;code&gt;j = next[j-1]&lt;/code&gt;），然后继续用当前的 &lt;code&gt;S[i]&lt;/code&gt; 和新的 &lt;code&gt;P[j]&lt;/code&gt; 比较。&lt;strong&gt;文本指针 &lt;code&gt;i&lt;/code&gt; 不动！&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="4-手把手模拟运行看清不回溯"&gt;4. 手把手模拟运行（看清“不回溯”）
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;文本 S&lt;/strong&gt;：&lt;code&gt;ABABABC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模式 P&lt;/strong&gt;：&lt;code&gt;ABABC&lt;/code&gt; (next = [0, 0, 1, 2, 0])&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;步骤详解&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;i=0, j=0&lt;/code&gt;：&lt;code&gt;S[0]=A&lt;/code&gt; vs &lt;code&gt;P[0]=A&lt;/code&gt; → 匹配，&lt;code&gt;i=1, j=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=1, j=1&lt;/code&gt;：&lt;code&gt;S[1]=B&lt;/code&gt; vs &lt;code&gt;P[1]=B&lt;/code&gt; → 匹配，&lt;code&gt;i=2, j=2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2, j=2&lt;/code&gt;：&lt;code&gt;S[2]=A&lt;/code&gt; vs &lt;code&gt;P[2]=A&lt;/code&gt; → 匹配，&lt;code&gt;i=3, j=3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3, j=3&lt;/code&gt;：&lt;code&gt;S[3]=B&lt;/code&gt; vs &lt;code&gt;P[3]=B&lt;/code&gt; → 匹配，&lt;code&gt;i=4, j=4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=4, j=4&lt;/code&gt;：&lt;code&gt;S[4]=A&lt;/code&gt; vs &lt;code&gt;P[4]=C&lt;/code&gt; → &lt;strong&gt;失配！&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;此时 &lt;code&gt;j=4 &amp;gt; 0&lt;/code&gt;，查 &lt;code&gt;next[3] = 2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;我们令 &lt;code&gt;j = 2&lt;/code&gt;。&lt;strong&gt;注意：&lt;code&gt;i&lt;/code&gt; 依然停在 4 不动。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;逻辑含义：模式串右移，利用已知的 &lt;code&gt;&amp;quot;AB&amp;quot;&lt;/code&gt; 前缀，直接对齐文本中已匹配的后缀 &lt;code&gt;&amp;quot;AB&amp;quot;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;继续比较：&lt;code&gt;S[4]=A&lt;/code&gt; 与 &lt;code&gt;P[2]=A&lt;/code&gt; → 匹配，&lt;code&gt;i=5, j=3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=5, j=3&lt;/code&gt;：&lt;code&gt;S[5]=B&lt;/code&gt; vs &lt;code&gt;P[3]=B&lt;/code&gt; → 匹配，&lt;code&gt;i=6, j=4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=6, j=4&lt;/code&gt;：&lt;code&gt;S[6]=C&lt;/code&gt; vs &lt;code&gt;P[4]=C&lt;/code&gt; → 匹配，&lt;code&gt;j == 5&lt;/code&gt;，&lt;strong&gt;匹配成功！&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="5-最难啃的骨头如何高效构建-next-数组"&gt;5. 最难啃的骨头：如何高效构建 Next 数组？
&lt;/h3&gt;&lt;p&gt;构建 Next 数组的过程，&lt;strong&gt;本质上是模式串自己与自己进行 KMP 匹配&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们用双指针：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;len&lt;/code&gt; 表示当前最长的相等前后缀长度（也是前缀指针）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i&lt;/code&gt; 从 1 遍历到末尾（后缀指针）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;构建逻辑（伪代码思路）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;next[0] = 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;len = 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;i = 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;while i &amp;lt; m:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if P[i] == P[len]:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; len += 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; next[i] = len
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; i += 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; else:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; if len &amp;gt; 0:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # 关键回退！利用已经算好的 next
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; len = next[len - 1]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; else:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; next[i] = 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; i += 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;手动演算 &lt;code&gt;P = &amp;quot;AAACAAAA&amp;quot;&lt;/code&gt;（让你体会回退的精髓）&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;i=1, len=0&lt;/code&gt;：P[1]=A vs P[0]=A → 匹配，&lt;code&gt;next[1]=1&lt;/code&gt;, &lt;code&gt;len=1&lt;/code&gt;, &lt;code&gt;i=2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=2, len=1&lt;/code&gt;：P[2]=A vs P[1]=A → 匹配，&lt;code&gt;next[2]=2&lt;/code&gt;, &lt;code&gt;len=2&lt;/code&gt;, &lt;code&gt;i=3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=3, len=2&lt;/code&gt;：P[3]=C vs P[2]=A → 失配！
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;len &amp;gt; 0&lt;/code&gt;，所以 &lt;code&gt;len = next[1] = 1&lt;/code&gt;（回退）。&lt;/li&gt;
&lt;li&gt;继续比较：P[3]=C vs P[1]=A → 失配！&lt;/li&gt;
&lt;li&gt;&lt;code&gt;len &amp;gt; 0&lt;/code&gt;，所以 &lt;code&gt;len = next[0] = 0&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;继续比较：P[3]=C vs P[0]=A → 失配！&lt;/li&gt;
&lt;li&gt;&lt;code&gt;len == 0&lt;/code&gt;，&lt;code&gt;next[3] = 0&lt;/code&gt;, &lt;code&gt;i=4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i=4, len=0&lt;/code&gt;：P[4]=A vs P[0]=A → 匹配，&lt;code&gt;next[4]=1&lt;/code&gt;, &lt;code&gt;len=1&lt;/code&gt; &amp;hellip; 以此类推。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="6-终极总结面试考试速记"&gt;6. 终极总结（面试/考试速记）
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;时间复杂度&lt;/strong&gt;：&lt;strong&gt;O(n + m)&lt;/strong&gt;（预处理 O(m)，匹配 O(n)）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;空间复杂度&lt;/strong&gt;：O(m)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心口诀&lt;/strong&gt;：

 &lt;blockquote&gt;
 &lt;p&gt;匹配失败看前一位，&lt;br&gt;
Next 值决定 j 回退到几，&lt;br&gt;
主串指针永不回头，&lt;br&gt;
前缀函数自己匹配自己。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;代码实现（Python）&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# 前缀长度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 回退&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;kmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next_arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# 返回起始索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 如果想找所有匹配：j = next_arr[j-1]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item></channel></rss>