标签:
Poj KMP 总结中给出了POJ中几乎所有KMP算法的思路。我挑选了其中几道难道适中的题目练习。如果读者还不清楚KMP算法的原理,可以看我的另外一篇文章:字符串匹配-KMP算法。
我们先给出KMP算法最基本的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int Next[10010];
char Pattern[10010],Text[1000010];
int m,n;
void compute_next(int *Next,char *Pattern)
{
int k=0;
Next[0]=Next[1]=0;
//Next[i]表示Pattern[0]到[i-1]的最大公共长度,k是Next[i-1]的值
for(int i=2;i<=m;i++)
{
for(;k!=0&&Pattern[k]!=Pattern[i-1];k=Next[k]);
if(Pattern[k]==Pattern[i-1]) k++;
Next[i]=k;
}
}
int kmp_match(char *Text,char *Pattern,int *Next)
{
int i=0,j=0,ans=0;
//i表示上一次迭代匹配了多少个字符
//j表示这次迭代从Text的哪个字符开始比较
//ans表示匹配成功的次数
while(j<n)
{
for(i=Next[i];i<m&&Pattern[i]==Text[j];i++,j++);
//直接从Next[q]的位置开始匹配
if(i==0) j++;
//如果一个也没有匹配j向后移动一位
else if(i==m) ans++;
//如果匹配成功ans++
}
return ans;
}
int main()
{
int N;
scanf("%d",&N);
while(N--)
{
scanf("%s %s",Pattern,Text);
m=strlen(Pattern),n=strlen(Text);
compute_next(Next,Pattern);
cout<<kmp_match(Text,Pattern,Next)<<endl;
}
}
POJ1961(POJ2406)
题目大意是给一个字符串,求这个字符串到第i个字符为止的循环节的次数。比如对于aabaabaabaab,长度为12,到第二个a时,a出现2次,输出2;到第二个b时,aab出现了2次,输出2;到第三个b时,aab出现3次,输出3;到第四个b时,aab出现4次,输出4。
对于题目中的例子,最后一个b的next值为9,观察一下就会发现实际上这个值指示了最后一个循环节开始的位置。因此按照我们的定义,i-next[i]表示从第1个字符到第i个字符所能构成的最短的循环节的长度。如果i能整除i-next[i],则说明整个字符串能由重复的循环节构成。当然,i不能等于i-next[i]。


#include<cstdio>
#include<iostream>
using namespace std;
int Next[1000010];
char Pattern[1000010];
int T=1,len,length;
void compute_next(int *Next,char *Pattern)
{
int k=0;
Next[0]=Next[1]=0;
for(int i=2;i<=len;i++)
{
for(;k!=0&&Pattern[k]!=Pattern[i-1];k=Next[k]);
if(Pattern[k]==Pattern[i-1]) k++;
Next[i]=k;
}
}
int main()
{
while(scanf("%d",&len)&&len)
{
scanf("%s",Pattern);
compute_next(Next,Pattern);
printf("Test case #%d\n",T++);
for(int i=1;i<=len;++i)
{
//循环节的长度
length=i-Next[i];
//如果有多个循环
if(i!=length&&i%length==0) printf("%d %d\n",i,i/length);
}
printf("\n");
}
}POJ2752#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int res[400010],Next[400010];
char Pattern[400010];
int m;
void compute_next(int *Next,char *Pattern)
{
int k=0;
Next[0]=Next[1]=0;
for(int i=2;i<=m;i++)
{
for(;k!=0&&Pattern[k]!=Pattern[i-1];k=Next[k]);
if(Pattern[k]==Pattern[i-1]) k++;
Next[i]=k;
}
}
int main()
{
while(scanf("%s",Pattern)!=EOF)
{
int len=strlen(Pattern);
m=len;
compute_next(Next,Pattern);
int i=0,j=0;
while(Next[m]!=0)
{
res[i]=Next[m];
m=Next[m];
i++;
}
sort(res,res+i);
for(j=0;j<i;j++) cout<<res[j]<<" ";
cout<<len<<endl;
}
}
POJ2541
这道题题意比较绕,翻译一下就是在文本T[1…n]中找一个最长的后缀(长度为m,m<=13),以这个后缀为匹配的模式P:T[n-m+1…n],找出P在文本T[1…n-1]出现的最后一个起始位置k(k<=n-m),若找到k,则得出T[n+1]为找到的模式T[k…k+m-1]的下一个位置,即T[n+1]=T[k+m]。如果m=13时找不到,则尝试m=12直到m=1;如果都找不到T[n+1]=0。对于样例:
10 7
1101110010
1101110010
1: 11011100100
2: 110111001001
3: 1101110010010
4: 11011100100100
5: 110111001001001
6: 1101110010010010
7: 11011100100100100
本题也可以用状态压缩dp的办法,这里我们不讨论。如果直接暴力枚举字符串的后面13个的字母,然后再用KMP匹配,这样的话,就分别要匹配后面的13,12,11....1个字母。但是如果把原来的字符串逆序转换一下,只需要求一次就够了。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXN=1002000;
const int START=2000;
char T[MAXN];
int Next[MAXN];
int N,L;
char compute_next(char *Pattern,int len)
{
int k=0;
int pos=-1,Max=-1;
Next[0]=Next[1]=0;
for(int i=2;i<=len;i++)
{
for(;k!=0&&Pattern[k]!=Pattern[i-1];k=Next[k]);
if(Pattern[k]==Pattern[i-1]) k++;
Next[i]=k;
if(Next[i]==13) return Pattern[i-Next[i]-1];
if(Max<Next[i])
{
Max=Next[i];
pos=i-Next[i]-1;
}
}
if(Max==-1) return '0';
return Pattern[pos];
}
int main()
{
while(scanf("%d%d%*c",&N,&L)!=EOF)
{
char *p;
char *str=T+START;
p=str;
gets(str);
//逆序转换
for(int i=0,j=N-1;i<N/2;++i,--j)
{
char temp=str[i];
str[i]=str[j];
str[j]=temp;
}
for(int i=0;i<L;++i)
{
*(str-1)=compute_next(str,N);
--str;
++N;
}
--p;
while(p>=str)
{
putchar(*p);
--p;
}
puts("");
}
}
POJ2185#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=10000+10;
int Next[N],cnt[N],n,m;
char Text[N][80];
void compute_next(int *Next,char *Pattern)
{
int m=strlen(Pattern);
Next[0]=-1;
for(int i=1;i<m;i++)
{
int j=Next[i-1];
while(j!=-1&&Pattern[j+1]!=Pattern[i]) j=Next[j];
if(Pattern[j+1]==Pattern[i]) j++;
Next[i]=j;
}
for(int i=0;i<m;i++) cout<<Next[i]<<endl;
}
void solve(int r)
{
Next[0]=-1;
for(int i=1;i<n;i++)
{
int j=Next[i-1];
while(j!=-1&&strcmp(Text[j+1],Text[i])) j=Next[j];
if(strcmp(Text[j+1],Text[i])==0) j++;
Next[i]=j;
}
printf("%d\n",(n-1-Next[n-1])*(r));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++)
{
scanf("%s",Text[i]);
compute_next(Next,Text[i]);
int j=m-1;
while(j!=-1)
{
cnt[m-1-Next[j]]++;
j=Next[j];
}
}
int r=0;
for(int i=1;i<=m;i++)
{
if(cnt[i]==n)
{
r=i;
break;
}
}
for(int i=0;i<n;i++) Text[i][r]=0;
solve(r);
}
}
标签:
原文地址:http://blog.csdn.net/qq_32400847/article/details/51318243