<span style="font-size:18px;">/************************************
KMP最初的代码(写next可能会CE)
1.getnext()递推
规定next[0]=-1;
当nextn[j]=k时:
若p[k]==p[j],可以看出next[j+1]=k+1
否则k=next[k]
2.KMP()
当比较到s[i]!=p[j]时,j=nextn[j],
省去了原串的回溯,复杂度O(m+n)
3.对nextn的理解
a.指针j要回溯的位置
b.有多长的前缀重复出现
**************************************/
void getnext()
{
int j=0,k=-1;
nextn[0]=-1;
while(j<lenp-1)
{
if(k==-1 || p[k]==p[j])
{
j++;
k++;
nextn[j]=k;
}
else k=nextn[k];
}
}
void KMP()
{
int i=0,j=0;
while(i<lens)
{
if(j==-1 || s[i]==p[j])
{
i++;
j++;
}
else j=nextn[j];
}
}</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">/*****************************************************************
题目链接http://acm.hust.edu.cn/vjudge/contest/123950#problem/A
KMP统计子串出现次数
******************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=10005,inf=1000005;
int next[maxn],cnt,lens,lenp;
char p[maxn],s[inf];
void getnext()
{
int j=0,k=-1;
next[0]=-1;
while(j<lenp-1)
{
if(k==-1 || p[k]==p[j])
{
j++;
k++;
next[j]=k;
}
else k=next[k];
}
}
void KMP()
{
int i=0,j=0;
while(i<lens)
{
if(j==-1 || s[i]==p[j])
{
i++;
j++;
}
else j=next[j];
if(j==lenp)//若成功,只移动j
{
cnt++;
j=next[j-1];
i--;
}
}
}</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">//上题hash解法,时间是KMP的两倍
</span><pre name="code" class="cpp">#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef unsigned long long ll;
const int maxs=1000005,maxp=10005;
char s[maxs],p[maxp];
int lens,lenp;
const ll b=100000007;
int cnt()
{
int i,ans=0;
ll t=1,sh=0,ph=0;
for(i=0;i<lenp;i++)
{
t*=b;
sh=sh*b+s[i];
ph=ph*b+p[i];
}
for(i=0;i+lenp<=lens;i++)
{
if(sh==ph) ans++;
if(i+lenp<lens)
{
sh=sh*b-s[i]*t+s[i+lenp];
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
getchar();
while(t--)
{
gets(p);
gets(s);
lens=strlen(s);
lenp=strlen(p);
printf("%d\n",cnt());
}
return 0;
}</span>
下一题题目链接:http://acm.hust.edu.cn/vjudge/contest/123950#problem/B<span style="font-size:18px;">/****************************
KMP求一个串的重复子串
****************************/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000005;
char p[maxn];
int lenp,next[maxn];
void getnext()
{
int j=0,k=-1;
next[0]=-1;
while(j<lenp)
{
if(k==-1 || p[k]==p[j])
{
j++;
k++;
next[j]=k;
}
else k=next[k];
}
}
int main()
{
int n,i,kase=1;
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
getchar();
gets(p);
lenp=strlen(p);
getnext();
printf("Test case #%d\n",kase++);
for(i=1;i<=lenp;i++)
{
if(next[i] && (next[i]%(i-next[i])==0))
{
printf("%d %d\n",i,next[i]/(i-next[i])+1);
}
}
printf("\n");
}
return 0;
}
</span>
<span style="font-size:18px;">/*******************************************************************
题目链接:http://acm.hust.edu.cn/vjudge/contest/123950#problem/D
KMP找所有前缀出现的次数
相当于每找到一个前缀重复出现都用next数组将
该前缀中包含的每一个前缀都计算一次
********************************************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=200005,mod=10007;
char p[maxn];
int lenp,nextn[maxn],cnt;
void getnext()
{
int j=0,k=-1;
nextn[0]=-1;
while(j<lenp)
{
if(k==-1 || p[k]==p[j])
{
j++;
k++;
nextn[j]=k;
}
else k=nextn[k];
}
}
int main()
{
int t,i,j;
scanf("%d",&t);
while(t--)
{
memset(nextn,0,sizeof(nextn));
scanf("%d",&lenp);
getchar();
gets(p);
getnext();
cnt=0;
for(i=1;i<=lenp;i++)
{
j=i;
while(j)
{
cnt=(cnt+1)%mod;
j=nextn[j];
}
}
printf("%d\n",cnt);
}
return 0;
}</span>
递归的过程中,当p[j]!=p[k],即不能等于k+1时,令k=next[k],只有这个时候才可能找到最长的,找有没有更小的一个串可以满足。如果找到一个地方的时候next[i]=0说明在这个i之前没有某个串能和整个串的一个前缀匹配,这时候k=next[k]=-1,在返回去加的时候就变成0,说明找不到了,即在j之前没有串能匹配一个前缀。
next[i]=0:说明i之前没有一个串能匹配前缀
next[i]=k:说明有长度为k的前缀可以匹配
next[0]=-1