POJ 1961/POJ 2406 /POJ 2752 /【KMP应用】

昨天把KMP复习了一下,顺便做了一些以前做过的题,貌似有那么点感觉O(∩_∩)O~
贴出来,方便以后复习,O(∩_∩)O~
http://poj.org/problem?id=1961
POJ 1961 Period
大意:
   定义字符串A,若A最多由n个相同字串s连接而成,则A=s^n,如"aaa" = "a"^3,"abab" = "ab"^2
   "ababa" = "ababa"^1
   
给出一个字符串A,求该字符串的所有前缀中有多少个前缀SA= s^n(n>1)
输出符合条件的前缀长度及其对应的n

  如aaa
  前缀aa的长度为2,由2个'a'组成
  前缀aaa的长度为3,由3个"a"组成

  分析:KMP
  若某一长度L的前缀符合上诉条件,则
    1.next[L]!=0(next[L]=0时字串为原串,不符合条件)
	2.L%(L-next[L])==0(此时字串的长度为L/next[L])

 对于2:有str[0]....str[next[L]-1]=str[L-next[L]-1]...str[L-1]
        =》str[L-next[L]-1] = str[L-next[L]-1+L-next[L]-1] = str[2*(L-next[L]-1)];
		假设S = L-next[L]-1;则有str[0]=str[s]=str[2*s]=str[3*s]...str[k*s],对于所有i%s==0,均有s[i]=s[0]
		同理,str[1]=str[s+1]=str[2*s+1]....
		      str[j]=str[s+j]=str[2*s+j]....
	    综上,若L%S==0,则可得L为str[0]...str[s-1]的相同字串组成,
		总长度为L,其中字串长度SL = s-0+1=L-next[L],循环次数为L/SL
        故对于所有大于1的前缀,只要其符合上述条件,即为答案之一

View Code
#include<stdio.h>
#include
<string.h>
const int N = 1000000+10;
char str[N];
int next[N];
int len;
void GetNext1(char str[N],int next[N])//寻找模式串的粗略next
{
int l=strlen(str);
next[
0]=-1;
int i=0,j=-1;
while(i<l)
{
if(j==-1||str[i]==str[j])
{
i
++;j++;
next[i]
=j;
}
else
j
=next[j];
}
}
int main()
{
int T = 0;
while(scanf("%d",&len)!=EOF)
{
T
++;
if(len == 0)break;
scanf(
"%s",str);
GetNext1(str,next);
int i;
printf(
"Test case #%d\n",T);
for(i=2;i<=len;i++)
{
if(next[i]!=0&&i%(i-next[i])==0)//保证不是原串且循环
{
printf(
"%d %d\n",i,i/(i-next[i]));
}
}

printf(
"\n");
}
return 0;
}

http://poj.org/problem?id=2406
POJ 2406 Power Strings
大意:给出一个字符串S,求该字符串最多是由相同重复字串连接而成的
若S由n个A组成,则称 S = A^n,求最大的n
  如 S=aaa,S="a"^3 => n = 3;
     S=ababa =>  n = 1
分析:KMP
   与POJ 1961类似,而且更简单:
    若L%(L-next[L])==0,n = L/(L-next[L]),else n = 1
View Code
#include<stdio.h>
#include
<string.h>
const int N = 1000000+10;
char str[N];
int next[N];
int len;
void GetNext1(char str[N],int next[N])//寻找模式串的粗略next
{
int l=strlen(str);
next[
0]=-1;
int i=0,j=-1;
while(i<l)
{
if(j==-1||str[i]==str[j])
{
i
++;j++;
next[i]
=j;
}
else
j
=next[j];
}
}
int main()
{
int T = 0;
while(scanf("%s",str)!=EOF)
{
if(strcmp(str,".")==0)break;
GetNext1(str,next);
int len = strlen(str);
if(len%(len-next[len])==0)
printf(
"%d\n",len/(len-next[len]));
else
printf(
"1\n");
}
return 0;
}

http://poj.org/problem?id=2752
POJ 2752 Seek the Name, Seek the Fame
大意:
给出一个字符串A,求A有多少个前缀同时也是后缀,从小到大输出这些前缀的长度

分析:KMP
对于长度为len的字符串,由next的定义知:
A[0]A[1]...A[next[len]-1]=A[len-next[len]]...A[len-1]此时A[0]A[1]...A[next[len]-1]为一个符合条件的前缀
有A[0]A[1]....A[next[next[len]]-1] = A[len-next[next[len] - next[next[len]]]...A[next[len]-1],故A[0]A[1]....A[next[next[len]]-1]也是一个符合条件的前缀
故从len=>next[len]=>next[next[len]] ....=>直到某个next[]为0均为合法答案,注意当首位单词相同时,也为答案。

View Code
#include<stdio.h>
#include
<string.h>
#include
<vector>
using namespace std;
vector
<int>ans;
const int N = 1000000+10;
char str[N];
int next[N];
void GetNext1(char str[N],int next[N])//寻找模式串的粗略next
{
int l=strlen(str);
next[
0]=-1;
int i=0,j=-1;
while(i<l)
{
if(j==-1||str[i]==str[j])
{
i
++;j++;
next[i]
=j;
}
else
j
=next[j];
}
}
int main()
{
while(scanf("%d",&str)!=EOF)
{
scanf(
"%s",str);
GetNext1(str,next);
int cur,len;
len
= strlen(str);
cur
= len;
ans.clear();
while(cur)
{
ans.push_back(cur);
cur
= next[cur];
}

if(str[0]==str[len])//首位字符相同
ans.push_back(1);
int sz = ans.size();
for(cur = sz-1;cur>0;cur--)
printf(
"%d ",ans[cur]);
printf(
"%d\n",ans[0]);
}
return 0;
}
原文地址:https://www.cnblogs.com/AndreMouche/p/1984772.html