Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 17439 | Accepted: 8404 |
Description
Input
number zero on it.
Output
Sample Input
3 aaa 12 aabaabaabaab 0
Sample Output
Test case #1 2 2 3 3 Test case #2 2 2 6 2 9 3 12 4
Source
题意:告诉你字符串的长度和字符串本身,要求对于每个字符串。输出所有重复前缀子串(连续出现次数>1),最后停留的索引位置,以及最大重复的次数。以“aabaabaabaab”为例子,索引停留在2时,存在“a”重复2次;3、4、5都没有前缀重复;索引停留在6时,存在“aab”重复2次;索引停留在“9”时,存在“aab”重复3次;索引停在12时,存在“aab”重复4次。
分析:这个题目容易让人联想到KMP算法。首先回忆一下next数组存放的是什么(当前位置最大前缀后缀匹配长度),假设next位置为i,那么next[i]表示将模式串向右移动这么多位置,在第i个位置的字母依旧可以匹配他本身。这道题不存在模式串和原始串,都是自身之内的比较。那么假设我们把“aabaabaabaab”的next数组算出来了,假设在位置i并且有连续的前缀情况下(这个是前提)——那么i-next[i]就是这个最大前缀的最小重复长度。当i=9时,next[i]=6(“aabaab”),也就是将这个字符串右移动6个字母位,第9位依旧可以匹配上,i-next[i]=3(“aab”),重复前缀为i/(i-next[i])=3。
还不理解的话,请记住,我们在求next的过程中就保证了存在重复前缀是i%(i-next[i])==0的充要条件。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN=1000000+10;
char s[MAXN];
int nex[MAXN];
void preKMP(){
int p=0,q=-1; nex[0]=-1;
while(s[p]){
while(q!=-1 && s[q]!=s[p]) q=nex[q];
nex[++p]=++q;
}
}
void KMP(){
preKMP();
for(int i=2;s[i-1];i++){
int t=i-nex[i];
if(i%t==0 && i/t>1) printf("%d %d\n",i,i/t);
}
}
int main()
{
int n,cnt=0;
while(scanf("%d",&n)&&n){
scanf("%s",s);
printf("Test case #%d\n",++cnt);
KMP();
printf("\n");
}
return 0;
}
还有不使用KMP算法的代码: 传送门
把这个题进行简化,一个长度为L的串,按照这题要输出在i[1...L]位置时,前缀循环节最大重复次数。
就把一个问题变成了L个子问题,从头开始找循环节(无需循环到末尾)也很好解决。
拥有巧妙的做题思路是多么的重要啊!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
#define N 1000010
char s[N];
int nextval[N];
int len;
void getnext(const char *s)
{
int i = 0, j = -1;
nextval[0] = -1;
while(i != len)
{
if(j == -1 || s[i] == s[j])
nextval[++i] = ++j;
else
j = nextval[j];
}
}
int main()
{
int T = 1;
int length, add;
while(scanf("%d", &len) && len)
{
scanf("%s", s);
getnext(s);
printf("Test case #%d\n", T++);
for(int i = 1; i <= len; ++i)
{
length = i - nextval[i]; //循环节的长度
if(i != length && i % length == 0) //如果有多个循环
printf("%d %d\n", i, i / length);
}
printf("\n");
}
return 0;
}