搞了几天的kmp,这个NEXT数组真心博大精深啊,到现在还有些next数组还不会用。
hdu 3336 Count the string 这道题就是对next数组的运用,可以得出一个公式,b[i]=b[next[i]]+1;把b数组全加起来就是所有前缀的和。
#include<iostream>
#include<cstring>
using namespace std;
int next[200001],n,b[200001],m;
char a[200001];
void getnext(char *p,int *next)
{
int i,j;
next[0]=-1;
i=0;
j=-1;
while(i<m)
{
if(j==-1||p[i]==p[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int i,sum;
scanf("%d",&n);
while(n--)
{
sum=0;
scanf("%d",&m);
scanf("%s",a);
getnext(a,next);
b[0]=0;
for(i=1;i<=m;i++)
{
b[i]=(b[next[i]]+1)%10007;
sum=(sum+b[i])%10007;
}
printf("%d\n",sum);
}
return 0;
}
hdu 1686 Oulipo 求子串在母串中出现的次数,也差不多是kmp模板题。。。
#include<stdio.h>
#include<string.h>
char a[10001],b[1000001];
int next[10001],x,y;
void getnext()
{
int i,j;
next[0]=-1;
i=0;
j=-1;
while(i<x)
{
if(j==-1||a[i]==a[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int kmp()
{
int i,j,sum=0;
i=0;j=0;
while(i<y)
{
if(j==-1||b[i]==a[j])
{
i++;
j++;
}
else
j=next[j];
if(j==x)
{
sum++;
j=next[j];
}
}
return sum;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
scanf("%s %s",a,b);
x=strlen(a);
y=strlen(b);
getnext();
printf("%d\n",kmp());
}
return 0;
}
hdu 1711 Number Sequence kmp模板题不解释。。。
#include<iostream>
using namespace std;
int n,m;
int a[1000001],b[10001];
int next[10001];
void getnext(int *p,int *next)
{
int i,j;
next[1]=0;
i=1;
j=0;
while(i<m)
{
if(j==0||p[i]==p[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int kmp(int *s,int *p)
{
int i,j;
i=1;
j=1;
getnext(p,next);
while(i<=n)
{
if(j==0||s[i]==p[j])
{
i++;
j++;
}
else
j=next[j];
if(j==m+1)
return i-m;
}
return -1;
}
int main()
{
int i,t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=m;i++)
scanf("%d",&b[i]);
printf("%d\n",kmp(a,b));
}
return 0;
}
hdu 1358 Period 对next数组的运用,求母串中重复子串的次数和首地址。。这个重复子串必须是连续的,像aabaababbaab,最后一个aab就不算啦。。还有一个求周期问题,len-next[i]为此字符串的最小循环节(i为字符串的结尾),另外如果len%(len-next[i])==0,此字符串的最小周期就为len/(len-next[i]);
#include<iostream>
#include<cstring>
using namespace std;
char a[1000001];
int next[1000001],n;
void getnext(char *p,int *next)
{
int i,j;
i=0;
j=-1;
next[0]=-1;
while(i<n)
{
if(j==-1||p[i]==p[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int i,j,x,y;
j=1;
while(~scanf("%d",&n)&&n)
{
getchar();
scanf("%s",a);
getnext(a,next);
printf("Test case #%d\n",j);
for(i=1;i<=n;i++)
{
x=i%(i-next[i]);
y=i/(i-next[i]);
if(x==0&&y>1)
printf("%d %d\n",i,y);
}
j++;
printf("\n");
}
}
hdu 3746 Cyclic Nacklace 求循环节问题,也是对next数组的运用。。。。
给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。
例子:
abcabc 已经循环2次,添加数为0
abcac 没有循环2次,添加字符abcac。数目为5.
abcabcab 已经循环过2次,但第三次不完整,需要添加数为1
#include<iostream>
#include<cstring>
using namespace std;
int n,next[100001],len;
char a[100001];
void getnext(char *p,int *next)
{
int i,j;
next[0]=-1;
i=0;
j=-1;
while(i<len)
{
if(j==-1||p[i]==p[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int i,j,x;
scanf("%d",&n);
while(n--)
{
scanf("%s",a);
len=strlen(a);
getnext(a,next);
j=len-next[len];
x=len%j;
//for(i=0;i<=len;i++)
// printf("%d ",next[i]);
if(j!=len&&x==0)
printf("0\n");
else
printf("%d\n",j-next[len]%j);
}
return 0;
}
hdu 2594 Simpsons' Hidden Talents 求第一个字符串的前缀与第二个字符串的后缀的最长公共子串,也是一道模板题。。。我这里用的事优化版的next数组,注意最后一个next数组的值。。。
#include<iostream>
#include<cstring>
using namespace std;
char a[50001],b[50001];
int next[50001],x,y;
void getnext()
{
int i,j;
next[0]=-1;
i=0;
j=-1;
while(i<x)
{
if(j==-1||a[i]==a[j])
{
i++;
j++;
if(a[i]!=a[j])
next[i]=j;
else
next[i]=next[j];
}
else
j=next[j];
}
}
int kmp()
{
int i,j;
j=0;
i=0;
getnext();
while(i<y)
{
if(j==-1||b[i]==a[j])
{
i++;
j++;
}
else
j =next[j];
}
return j;
}
int main()
{
int i,n,j,k;
while(cin>>a)
{
cin>>b;
x=strlen(a);
y=strlen(b);
n=kmp();
if(n==0)
printf("0");
else
{
for(i=0;i<n;i++)
printf("%c",a[i]);
printf(" %d",n);
}
printf("\n");
}
return 0;
}
下面是poj上的,真心觉得hdu上的题目难度系数大的到poj上就成了水题啦。。。我基本做的都是水题,学这个算法,这些题来做还是很好理解的,容易上手。。现在从hdu转到poj上做啦。。。。
poj 2406 Power Strings 求周期。。
#include<iostream>
#include<cstring>
using namespace std;
char a[1000001];
int next[1000001],len;
void getnext()
{
int i,j;
next[0]=-1;
i=0;j=-1;
while(i<len)
{
if(j==-1||a[i]==a[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int x,y;
while(~scanf("%s",a))
{
if(a[0]=='.')
break;
len=strlen(a);
getnext();
x=len-next[len];
y=len%x;
if(y==0)
printf("%d\n",len/x);
else
printf("1\n");
}
return 0;
}
poj 2752 Seek the Name, Seek the Fame 字符串前缀子串等于后缀子串,输出前缀最后一个字母的位置。。next数组的运用。。。
#include<iostream>
#include<cstring>
using namespace std;
char a[400001];
int next[400001],len,ans[400001];
void getnext()
{
int i,j;
next[0]=-1;
i=0;j=-1;
while(i<len)
{
if(j==-1||a[i]==a[j])
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int i,j;
while(~scanf("%s",a))
{
j=0;
len=strlen(a);
getnext();
i=next[len];
if(i!=0)
ans[j++]=i;
while(next[i]>0)
{
i=next[i];
ans[j++]=i;
}
for(i=j-1;i>=0;i--)
printf("%d ",ans[i]);
printf("%d\n",len);
}
return 0;
}
暂时只做了这些很水的题目,多效的题现在对于自己还有点苦难,加油