poj 2406 KMP算法

来源:互联网 发布:舌头乐队知乎 编辑:程序博客网 时间:2024/05/15 06:08

题目大意:定义字符串的n次幂为该字符串重复n次并续接起来。例如,假设字符串a="abc",则a^2="abcabc"。要求对每个输入的字符串s,求使s=a^n成立的最大的n。其中a是某个字符串。比如说,对“aaaa”来说,n=4;对于“ababab”,n=3。

题意很明确,就是求一个字符串的最小重复单元的重复次数n。如果一个字符串的最小重复单元是自身,那么n=1。s的最大长度是一百万,可见直接枚举一定会超时。这时可以借助KMP算法中的next数组。

next[i]的大小表示在字符串s的i位置之前,最长有多长的子串与s的前缀相同。应当注意的是,定义中所称的“子串”不能与“s的前缀”相同。举个例子,正如下表所示,字符串s="ababab"所对应的next数组是next[]={-1,0,0,1,2,3,4}。注意,next数组最后一个元素是4而不是6,这正是因为“子串”不能与“s的前缀”相同的缘故。

ababab\0-1001 234

怎样从next数组得知最小重复单元的个数呢?从下图可以直观地看出来。


 上面图中每个小格代表一个长为m的最小重复单元,整个字符串包括了(L+1)个这样的最小重复单元,所以长度为strlen(s) = (L+1)*m。显然,此字符串对应的next数组的最后一个元素将是next[strlen(s)] = L*m。这样,只要将两者相减即可求得m的大小,即m = strlen(s) - next[strlen(s)]。已知最小重复单元的长度m之后,只要用字符串的长度strlen(s) 除以m即可得到最小重复单元的重复次数。

假如字符串s的最小重复单元就是它自身呢?这种情况等价于strlen(s) 不是(strlen(s) - next[strlen(s)])的倍数。于是只要判断strlen(s) 模(strlen(s) - next[strlen(s)])是否为零即可。

#include <stdio.h>#include <stdlib.h>#include <string.h>#define LOCAL//求一个字符串的最小重复单元的重复次数//m = len-next[len],若len是m的倍数,则m是最小重复单元的长度,len/m是其重复次数//否则str的最小重复单元就是str自身//需要深入理解KMP算法char s[1000009];int next[1000009];void get_next(const char t[],int next[]){    int i = 1,k = 0;    next[0] = -1;    while(t[i] != '\0')    {        next[i] = k;        if(t[i] == t[k])        {            k++;        }else if(t[i] != t[0])        {            k = 0;        }else            k = 1;        i++;    }    next[i] = k;}int main(){    int len,m;    #ifdef LOCAL    if(NULL == freopen("t.txt","r",stdin))        printf("error redirecting\n");    #endif // LOCAL    while(1)    {        scanf("%s",s);        if(0 == strcmp(s,"."))            break;        get_next(s,next);        len = strlen(s);        m = len - next[len];        if(0 == m)            printf("1\n");        else if(0 == len%m)            printf("%d\n",len/m);        else            printf("1\n");    }    return 0;}






原创粉丝点击