KMP 主要用于求解以下问题:
求字符串 \(t\) 在字符串 \(s\) 中出现的所有位置。
如果存在 \(s[i \dots j]\) 与 \(t\) 完全相同,则称 \(t\) 在位置 \(i\) 出现了。
用 \(s[l \dots r]\) 表示字符串 \(s\) 的第 \(l\) 个字符到第 \(r-1\) 个字符所组成的字符串,下标从 \(1\) 开始。用 \(\operatorname{len}(s)\) 表示字符串 \(s\) 的长度。
设 \(fail_i\) 表示 \(s2[1 \dots i+1]\) 的前 \(fail_i\) 个字符与 \(s2[1 \dots i+1]\) 的后 \(fail_i\) 个字符完全相同。特别的,\(fail_1=0\)。如 \(s2=\)1234123
,那么 \(fail=\{0,0,0,0,1,2,3\}\)。这样在匹配到 \(s2\) 串的第 \(i\) 位的时候,如果匹配失败,就继续用 \(s2\) 的第 \(fail_{i-1}\) 位匹配。时间复杂度接近 \(\mathcal{O}(N+M)\)。
那么如何计算 \(fail\) 数组呢?
如果已经处理到第 \(i\) 位,先让 \(j=fail_{i-1}\),表示 \(s2[1 \dots j+1]=s2[i-j+1 \dots i-1]\)。
如果 \(s2_i \ne s2_{j+1}\),就让 \(j=fail_j\),因为这个子串的前 \(fail_j\) 个字符和后 \(fail_j\) 个字符完全相同。然后一直这样判断。直到 \(s2_i=s2_{j+1}\) 或 \(j=0\) 就结束。接下来判断现在这一位是否匹配,也就是判断 \(fail_i=fail_{j+1}\)。如果是,就让 \(j\) 加一。
让 \(fail_i=j\),这一位就计算完了。
如字符串 abacabacd
。初始时 \(j=0\)。步骤如下。
- \(i=2\)
\(s_i \ne s_{j+1}\),所以 \(fail_2 \leftarrow j=0\)。
- \(i=3\)
\(s_i=s_{j+1}\),所以 \(j \leftarrow j+1=1,fail_3 \leftarrow j=1\)。
- \(i=4\)
\(j \leftarrow fail_j=0\)。
\(s_i \ne s_{j+1}\),所以 \(fail_4 \leftarrow j=0\)。
- \(i=5\)
\(s_i=s_{j+1}\),所以 \(j \leftarrow j+1=1,fail_5 \leftarrow j=1\)。
- \(i=6\)
\(s_i=s_{j+1}\),所以 \(j \leftarrow j+1=2,fail_6 \leftarrow j=2\)。
- \(i=7\)
\(s_i=s_{j+1}\),所以 \(j \leftarrow j+1=3,fail_7 \leftarrow j=3\)。
- \(i=8\)
\(j \leftarrow fail_j=1\)
\(j \leftarrow fail_j=0\)
\(s_i \ne s_{j+1}\),所以 \(fail_8 \leftarrow j=0\)。
此时 \(fail\) 数组就计算出来了。
接着要进行匹配。匹配和处理的代码很像,如果第 \(i\) 位匹配失败了,就继续用第 \(fail_i\) 位进行匹配。如果 \(j=\operatorname{len}(s2)\),就说明 \(s2\) 在 \(s1\) 的第 \(i-j+1\) 位出现了。
这就是 KMP
算法。时间复杂度约为 \(\mathcal{O}(N+M)\)。
本题为模板题。
标签:dots,匹配,leftarrow,s2,KMP,fail,个字符 From: https://www.cnblogs.com/lrxmg139/p/18012094