poj 2406 KMP算法

题目大意:定义字符串的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
-100234

怎样从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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值