http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html
https://www.acwing.com/solution/content/23907/
先上两个大佬的博客
能懂基本的思想了
第二个图来自acw的第二个题解
看到这里应该就能明白了
OK了,了解到这里就能去听y总的课了...
next[i]表示以i为终点的后缀与和从1开始的前缀相等且这个后缀的长度最长,其实也就是之前博客里又说的匹配表
把子串单独拎出来看
然后移动之后就又开始看看下一个点是否匹配了
好了,现在过程都懂了,开始写代码:
主要分为两个部分:
第一个部分:字符串的匹配
这个是acw下面的一个评论,本来刚开始还不是很理解,现在很理解了
//KMP的匹配过程 for(int i=1;j=0;i<=m;i++) { while(j&&s[i]!=p[j+1]) j=ne[j];//这是一个往后退的过程,找到一个类似于前缀的另一个与s串匹配的位置 if(s[i]==p[j+1]) j++;//如果退无可退之后还不满足条件,那就到i的下一个位置 if(j==n) { //完成匹配 } }
NND,终于懂了.....
刷了三天,知道是个啥意思了,hhh
#include<iostream> using namespace std; const int N=100010,M=1000010; char q[N],s[M]; int ne[N];//保存next数组 int main() { int n,m; cin>>n>>q+1>>m>>s+1;//下标均从1开始 for(int i=2,j=0;i<=n;i++) //j表示匹配成功的长度,i表示q数组中的下标,因为q数组的下标是从1开始的,只有1个时,一定为0,所以i从2开始 { while(j&&q[i]!=q[j+1]) j=ne[j]; //如果不行可以换到next数组 if(q[i]==q[j+1]) j++; //成功了就加1 ne[i]=j; //对应其下标 } //j表示匹配成功的长度,因为刚开始还未开始匹配,所以长度为0 for(int i=1,j=0;i<=m;i++) { while(j&&s[i]!=q[j+1]) j=ne[j]; //如果匹配不成功,则换到j对应的next数组中的值 if(s[i]==q[j+1]) j++;//到退无可退的时候,就进行下一个i //匹配成功了,那么j就加1,继续后面的匹配 if(j==n)//如果长度等于n了,说明已经完全匹配上去了 { printf("%d ",i-j); //因为题目中的下标从0开始,所以i-j不用+1; j=ne[j]; //为了观察其后续是否还能跟S数组后面的数配对成功 } } return 0; }
代码还有点小细节懂的不是很彻底,再刷刷吧...