1.自然语言描述
KMP 算法是用来寻找模式串S中的模板串P,看是否存在,或P在S中出现了多少次;暴力法解决字符串匹配问题时,当P在S的当前位置不能匹配时,向后移动一位继续匹配,每次都从P的开头进行匹配,时间复杂度O(nm)级别;可以发现暴力法中P不断向后移的过程中,没有利用之前匹配的信息,这些信息可以让P向后移动更多的距离,也就是利用next数组,每次不一定非要从P的开头进行匹配,KMP 利用模板串P每个位置上最大的前缀后缀相同长度来节省时间。
KMP 算法的核心是next数组。next数组的含义是:设当前位置为i,next[i]={P(1,i)前缀和后缀相同时的最大长度} i∈(1,n) n为P长度。
2.代码描述
题目:Acwing.831 KMP字符串题目链接
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=1e6+10;
int ne[MAXN],n,m;
char s[MAXN],p[MAXN];
int main(void)
{
cin>>n>> p+1>>m>> s+1;
//求next数组
for(int i=2,j=0;i<=n;i++){//i从2开始,因为只有第一个元素的子串不含非平凡前后缀。
while(j&&p[i]!=p[j+1]) j=ne[j];
//这里对应了两种情况结束循环(匹配)
//1.如果始终找不到合适的公共前后缀使得i和j+1相等,用完了ne的信息,则说明(1,i)这个子串没有公共前后缀(ne[i]=0)
//2.如果因为i和j+1相等跳出循环,则说明(1,i)这个字串的最大公共前后缀长度为(1,j+1)的长度(纸上画图可以很清楚的表现出来)
// 那么就可以更新ne[i]了,ne[i]=j+1;
if(p[j+1]==p[i]) j++;
ne[i]=j;
}
//和求next数组时一样的匹配方法
for(int i=1,j=0;i<=m;i++){
while(j&&s[i]!=p[j+1]) j=ne[j];
//类似的
//1.j==0时,S(i-j+1,i)中没有能够与P匹配的子串
//2.S(i-j+1,i)与P(1,j+1)匹配了
if(p[j+1]==s[i]) j++;
//如果j+1后得到的j==n,则说明S(i-j+1,i)与P串完全匹配了
if(j==n){
printf("%d ",i-n);
j=ne[j];//P可能在S中不只出现一次,所以要接着滑动P串继续匹配
}
}
return 0;
}