Uvalive 3026 - Period(next数组的应用)

题目链接 https://cn.vjudge.net/problem/UVALive-3026

【题意】
给定长度为n的字符串,n<=1e6,求它每个周期前缀的最短循环节对应的循环次数。
【思路】
KMP算法中next数组的简单应用,比如说第二组样例,字符串”aabaabaabaab”的情况如下

i 0 1 2 3 4 5 6 7 8 9 10 11 12
p[i] a a b a a b a a b a a b \0
next[i] -1 0 1 0 1 2 3 4 5 6 7 8 9

可以观察到当某个前缀s[0,i]是周期性的前缀当且仅当(i+1)%(next[i+1]-(i+1))==0,同时next[i+1]是要大于0,为什么是这样的呢,原因就在于next数组的体现的功能是什么了,next[i]表示的是s[0,i-1]中最大的相同前缀后缀长度,举个例子,假设下面是一个字符串,由五个部分构成,每部分对应一个子串,每个子串长度是len,总长度lens=5*len

字符串s 0 1 2 3 4

假设现在next[lens]=4*len,说明前缀s[0,1,2,3]和后缀s[1,2,3,4]完全相同,进一步可以得到s[0]=s[1]=s[2]=s[3]=s[4],也就是说循环节的长度就是lens-next[lens],当然要保证长度可以整除并且这个前缀不能为空,所以就有了刚刚的两个条件了,当某个前缀s[0,i]是周期性的前缀当且仅当(i+1)%(next[i+1]-(i+1))==0

#include<cstdio>
#include<cstring>
using namespace std;

const int maxn=1000050;

int lenp;
char p[maxn];
int next[maxn];

void getnext(){
    next[0]=-1;
    int k=-1,j=0;
    while(j<lenp){
        if(-1==k || p[k]==p[j]){
            ++k;
            ++j;
            next[j]=k;
        }
        else k=next[k];
    }
}

int main(){
    int kase=0;
    while(scanf("%d",&lenp)==1 && lenp){
        scanf("%s",p);
        getnext();
        printf("Test case #%d\n",++kase);
        for(int i=2;i<=lenp;++i){
            if(next[i]>0 && i%(i-next[i])==0){
                printf("%d %d\n",i,i/(i-next[i]));
            } 
        }
        puts("");
    }
    return 0;
}
posted @ 2018-04-08 23:40  不想吃WA的咸鱼  阅读(104)  评论(0编辑  收藏  举报