您的位置:首页 > 其它

HDU 1358 Period(KMP求前缀子串的循环次数)

2015-08-19 20:43 471 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1358

题目大意:给定一个字符串s,然后对于所有的前缀子串,如果是一个周期循环的子串,那么就输出他的长度和循环了几次。

解:如果长度为k的串是以t为周期循环的,那么之后存在一个i满足next[i] * 2 > l,即超过一半的长度且next[i] = k。那么可以知道i串的循环周期是和n长度为k的串一致。 举例说明如下:看不懂上面那句话的,直接看下面的例子吧。

i 0 1 2 3 4 5 6 7 8 9

char a b c a b c a b c

next 0 0 0 0 1 2 3 1 5 6

然后当i= 6时,有next[i] * 2 = i,表明前面的一个串循环了两次,且周期week[i] = next[i] = 3,

当 i=8时,有 next[i] * 2 > i,next[i] = 5;但是week[5] = 0,就是长度为5的串没有周期。所以i=8也没有周期

i=9, 有next[i] * 2> i, next[i] = 6; week[6]的周期为3,所以week[i]=3.

这个可以这样看,i=6时,有abcabc。i=9时是abcabcxxx,因为abcabc = abcxxx(由next数组可知),那么xxx就一定是abc,也就是一个周期。

那么最后只要周期长度大于0的输出就可以了,具体重复了几次周期就是i / week[i]。

网上的方法没有用到数组,当然自己做的时候没想太多哈。

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
char a[1000005];
int Next[1000005];
int week[1000005];
void getNext(int len)
{
int i = 0, j = -1;
Next[0] = -1;
while(i < len) {
if(j == -1 || a[i] == a[j]) {
Next[++i] = ++j;
}
else {
j = Next[j];
}
}
}
int main ()
{
int n, cas = 1;
while (scanf("%d", &n), n) {
scanf("%s", a);
memset(week, 0, sizeof(week));
printf("Test case #%d\n", cas++);
getNext(n);
for (int i = 2; i <= n; i ++) {
if(Next[i] * 2 == i) {
week[i] = Next[i];
}
if(Next[i] * 2 > i) {
week[i] = week[Next[i]];
}
if(week[i] > 0) {
printf("%d %d\n", i, i / week[i]);
}
}
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: