[AcWing]831. KMP字符串(C++实现)KMP模板题
1. 题目
2. 读题(需要重点注意的东西)
思路:KMP的主要思路是如何通过已经比较的数,来减少两个字符串比较的次数。
接下来我会分析为什么可以减少比较的次数,这就是KMP的核心思想。
比较难以理解的地方就在于跳转的操作,这个操作只与p串本身有关,而与s串无关
跳转的操作本质上是一个求最大的相同前后缀长度的过程,每个字符串最大的相同前后缀长度即为p[i]不匹配时为跳
转的地方,如p串为ababab时(下标从1开始):
ne[1] = 0;
ne[2] = 0; 最大的前缀为a,后缀为b
ne[3] = 1; 前缀为ab,后缀为ba时不等;前缀为a,后缀为a时相等,因此最大的相同前后缀长度为1
ne[4] = 2; 前缀为aba,后缀为bab时不等;前缀为ab,后缀为ab时相等,因此最大的相同前后缀长度为2
同理:
ne[5] = 3;
ne[6] = 4;
那么为什么求每个位置最大相同前后缀长度能减少比较次数呢?
我们看这样一种情况:(整个过程的视频演示在后面) 当比较到这样一种情况时:s[i] 与 p[j+1]不等了 ( 此时j=5 )
执行跳转操作,j = ne[5] = 3 ,相当于整个p串向后移动了 再比较 s[i] 和 p[j+1 = 4]的大小
我们可以看到,跳转后,绿色框中的值天生就是是完全相同的,不用再比较了 此时也只需要比较一位数即可,不用重新全部比较一遍 还是不同,再根据j = ne[3] = 1 跳转;比较s[i] 和 p[ j+1 = 2 ]
比较此时的s[i] 和 p[j+1]的大小,又失败了,那么直到跳转到j = 0,说明比较失败。
i此时再向前移动一位进行比较
---------------------------整个跳转过程的视频演示-------------------------------------
发现了吗?
i 只有当 ne 全部跳转完时,才会继续走到下一位进行比较。因此,只需要遍历一遍s串,就能够知道p串是否能匹配s串的子串。
这样,仅仅通过跳转,就减少了字符串比较的次数。
3. 解法
---------------------------------------------------解法---------------------------------------------------
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
int ne[N];
char s[M], p[N];
int main()
{
cin >> n;
for(int i = 1;i <= n;i++) cin >> p[i];
cin >> m;
for(int i = 1;i <= m;i++) cin >> s[i];
for (int i = 2, j = 0; i <= n; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == n) printf("%d ", i - n);
}
return 0;
}
4. 可能有帮助的前置习题
5. 所用到的数据结构与算法思想
6. 总结
KMP算法模板题,推荐完全背下来。
|