(简单) POJ 1961 Period,扩展KMP。

Description

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as AK ,that is A concatenated K times, for some string A. Of course, we also want to know the period K.

 

  题目就是给一个字符串,然后让对从2到N的所有前缀字符串,找出里面的重复串。

  对于找重复串的问题,用扩展KMP就可以解决。然后就是对于每个前缀,可以证明后面的前缀的最小重复串的长度一定大于等于前面的,因为形成重复串的必要条件就是i+next1[i]>=length 这样的话如果前面不符合这个式子,后面就更不用说了,必须让i增大才可能。

 

代码如下:

// ━━━━━━神兽出没━━━━━━
//      ┏┓       ┏┓
//     ┏┛┻━━━━━━━┛┻┓
//     ┃           ┃
//     ┃     ━     ┃
//     ████━████   ┃
//     ┃           ┃
//     ┃    ┻      ┃
//     ┃           ┃
//     ┗━┓       ┏━┛
//       ┃       ┃
//       ┃       ┃
//       ┃       ┗━━━┓
//       ┃           ┣┓
//       ┃           ┏┛
//       ┗┓┓┏━━━━━┳┓┏┛
//        ┃┫┫     ┃┫┫
//        ┗┻┛     ┗┻┛
//
// ━━━━━━感觉萌萌哒━━━━━━

// Author        : WhyWhy
// Created Time  : 2015年07月18日 星期六 10时01分22秒
// File Name     : 1961.cpp

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>

using namespace std;

const int MaxN=1000006;

void EKMP_pre(int m,char s[],int next1[])
{
    int p=0,a=1,L;

    next1[0]=m;

    while(p+1<m && s[p]==s[p+1])
        ++p;

    next1[1]=p;

    for(int k=2;k<m;++k)
    {
        L=next1[k-a];
        p=next1[a]+a-(next1[a]!=0);

        if(k+L-1<p)
            next1[k]=L;
        else
        {
            ++p;

            while(p<m && s[p]==s[p-k])
                ++p;

            next1[k]=p-k;
            a=k;
        }
    }

    next1[m]=0;
}

int next1[MaxN];
char s[MaxN];
int N;

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    
    int cas=1;
    int minn;

    while(~scanf("%d",&N) && N)
    {
        minn=1;
        scanf("%s",s);

        EKMP_pre(N,s,next1);

        printf("Test case #%d\n",cas++);

        for(int i=1;i<N;++i)
        {
            while(minn<=i && next1[minn]+minn<i+1)
                ++minn;

            if(minn<=i && (i-minn+1)%minn==0)
                printf("%d %d\n",i+1,(i-minn+1)/minn+1);
        }

        puts("");
    }
    
    return 0;
}
View Code

 

posted @ 2015-07-18 10:52  WhyWhy。  阅读(134)  评论(0编辑  收藏  举报