先写next[]数组的,依据的算法来自胡凡的《算法笔记》p457,要结合里面的图理解:
首先要明白:
1.缀能节省时间,缀越长节省的时间越多,我们要尽可能使用长缀
2.前缀后缀只是相对的说法,譬如一组前后缀,前缀头后缀头一左一右
3next[末尾]表示前缀的最后一位,即前缀的长度。通常来讲,next[i],i是前缀的下标,也是这个字串(也可以是某个字串的字串,无论如何,前缀一定是整个字串的首部)
4前缀和后缀表现在字串上是一模一样的。
5.next[i]=k的意义就是:给一个字串s[0.....i] ,使得前缀s[0.....k]等于后缀s[i-k.....i]的最大k;
**给出一个字串求里面的最大前(后)缀的方式就是next[末尾],前缀是去匹配的,后缀是待匹配的
现在开始求解:
(1).设目标为k+1,前一位是k,已经匹配;
若 在后缀中:S[k+1] == 在前缀中: S[next[k]+1] 则是匹配成功的。
(2).若没有匹配成功,下面的前缀要往左滑,滑到什么位置呢?那就是 把下面的前缀当作后缀,从里面找最长前缀试图去匹配S[k+1]。即令j=next[k],然后判断 后缀中:S[k] == 在前缀中: S[next[j]+1] 。如果还没有成功,那就继续j=next[j]......所以这是个循环。
例题1:(poj用不了<bits/stdc++.h>头文件)
http://poj.org/problem?id=3461
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1e6+10;
char w[maxn],t[maxn];
int Next[maxn];
void getNext(char s[],int m)
{
Next[0]=-1;
int j=-1;
for(int i=1;i<m;i++)
{
while(j!=-1 && s[i]!=s[j+1])
j=Next[j];
if(s[i]==s[j+1])
j++;
Next[i]=j;
}
}
int kmp(char test[],char pattern[])
{
int n=strlen(test),m=strlen(pattern);
getNext(pattern,m);
int ans=0;
int j=-1;
for(int i=0;i<n;i++)
{
while(j !=-1 && test[i] != pattern[j+1])
j=Next[j];
if(test[i]==pattern[j+1])
j++;
if(j==m-1)
{
j=Next[j];
ans++;
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
// fill(Next,Next+maxn,0);
scanf("%s%s",w,t);
printf("%d\n",kmp(t,w));
}
return 0;
}
例题2
http://poj.org/problem?id=2752
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=4e5+10;
int Next[N],ans[N];
char s[N];
void getNext(char s[],int m)
{
int j=-1;
Next[0]=-1;
for(int i=1;i<m;i++)
{
while(j!=-1 && s[i] != s[j+1])
j=Next[j];
if(s[i]==s[j+1])
j++;
Next[i]=j;
}
}
int main(){
while(~scanf("%s",s)){
int len=strlen(s);
getNext(s,len);
int cnt=0;
int j=Next[len-1];
while(j>=0){
ans[++cnt]=j+1;
j=Next[j];
}
for(int i=cnt; i>0; i--)printf("%d ",ans[i]);
printf("%d\n",len);
}
return 0;
}