字符串循环节 POJ2406 Power Strings 为例

题意:找到字符串一个循环节使其循环次数最大,输出最大循环次数,没有循环节输出1

题解:KMP的next数组,next[ i ]表示前 i 个字符相同前后缀的最大长度,所以next[ len ]表示整个字符串的相同前后缀的最大长度,如果 len % (len-next[len])==0,len-next[len]就是最小循环节的长度了,答案即为len / (len-nx[len]),否则最小循环节长度为len,答案为1;

简单证明一下:若 len%(len-nx[len])==0,则整个字符串相同前后缀的最大长度一定大于等于len/2,等于len/2的显然,略过不表,大于len/2,表示前后缀有公共部分,那就会出现两种情况:

第一种

如图所示,L表示最大后缀的起点,R表示最大前缀的终点,S表示len-next[len],显然满足 len % (len-next[len])==0,答案即为len / (len-nx[len])

第二种:

如图所示,较之上图,公共前后缀多了一个尾巴T,显然这个T一定是S的一段前缀,这显然不满足len % (len-next[len])==0,所以答案为 1

好了 扯完了,写这篇主要不是为了写这道题的题解,只是想记录一下这个方法和思想,顺便写一下题解=。=

题目很简单,但是还是贴一下代码吧

#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1000010;
char s[maxn];
int nx[maxn];
void getNext(char * p)
{
    nx[0] = -1;
    int i = 0, j = -1;
    int len=strlen(p);
    while (i < len)
    {
        if (j == -1 || p[i] == p[j])
        {
            ++i;
            ++j; 
            nx[i] = j;
        }    
        else
            j = nx[j];
    }
}
int main()
{
    while(scanf("%s",s)&&s[0]!='.')
    {
        getNext(s);
        int len=strlen(s);
        printf("%d\n",len%(len-nx[len])?1:len/(len-nx[len]));
    }
    return 0;
}

 

posted @ 2019-08-13 15:23  Zeronera  阅读(346)  评论(0)    收藏  举报