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

来源:互联网 发布:哪种编程语言范围广 编辑:程序博客网 时间:2024/06/06 09:22

题目链接: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;}


0 0
原创粉丝点击