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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值