POJ - 2406 Power Strings解题报告(KMP,字符串划分成若干连续相同子串)

题目大意:

好像就是说,好多组测试数据,每组测试数据就是给你一串字符串,然后让你找出一个最短的子串,这个子串满足条件:若干个该子串连接就能组成原字符串。也就是让你想办法把所给字符串划分成尽量短的若干相同子串。

分析:

策略:

next [ i ] 表示 a [ 0 ] 到 a [ i - 1] 的最长相同前后缀长度。那么如果 n % ( n - next [ n ] ) == 0 ,则最短子串为 n - next [ n ] ;否则为 n。

该策略正确性证明:

首先证明若能被整除,那么原串一定可以划分为该子串:

设原串为: a0a1a2.....an1 。n - next [ n ] = t 。n=kt(k∈ N+ )
则可说明:
a0a1a2.....ant = atat+1at+2.....an
即: ai=ai+t ; t 为该串的一个周期。又因为能被整除,所以正好是 k 个满周期。

下面再证明:如果 n % ( n - next [ n ] ) != 0 ,则一定不存在长度小于n的满足要求的子串。

用反证法,假设存在一个满足要求的长度为 m 的子串(m < n 且 n%m = 0)且 n % ( n - next [ n ] ) ! = 0 。那么设 t = n - next [ n ] 。
显然:
ai=ai+t ai=ai+m ( t < m )对于任意的不超界的 i 都成立,并且原字符串可分为 k 个满周期的长度为m的子字符串。
这时就可以得到:
ai=ai+mxt (m > xt)对于任意的不超界的 i 都成立。那么一定就存在一个整数 x 可以使得 0 < m - xt < t;那先在我就让 x 取这一个值。这样我就得到了一个比 t 更小的子字符串,也就相当于得到了一个比 a0a1a2.....ant 更长的相同前后缀 a0a1a2.....antant+1.....an(mxt) 。这与 next 数组的含义相矛盾,因为按照 next [ n ] 的值,最长的相同前后缀长度应该是 n-t 。所以假设不成立,证毕~

现在终于证明完毕该策略的正确性,然后理论上才能用。

代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#define maxn 1000500
using namespace std;

char c[maxn]={0};
int my_next[maxn];
int n;

void get_next()
{
    int i=0,j=-1;
    my_next[0]=-1;
    while(i<n)
    {
        if(j!=-1&&c[i]!=c[j])
        {
            j=my_next[j];
            continue;
        }
        i++;j++;
        my_next[i]=j;
    }
}
void ceshi()
{
    for(int i=0;i<=n;i++)
    {
        cout<<my_next[i]<<" ";
    }
    cout<<endl;
}
int main()
{
    while(1)
    {
        scanf("%c",&c[0]);
        if(c[0]=='.')break;
        n=1;
        while(c[n-1]!='\n')
        {
            scanf("%c",&c[n]);
            n++;
        }
        n--;
        get_next();
        //ceshi();
        //cout<<"n="<<n<<" "<<"next[n]="<<my_next[n]<<endl;
        if(n%(n-my_next[n])==0)cout<<n/(n-my_next[n])<<endl;
        else cout<<"1"<<endl;
    }
}

后注:

在网上找了半天都没有找到一篇证明上述策略正确性的文章,不知道这我一篇会不会访问量比较高。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值