题目大意:
好像就是说,好多组测试数据,每组测试数据就是给你一串字符串,然后让你找出一个最短的子串,这个子串满足条件:若干个该子串连接就能组成原字符串。也就是让你想办法把所给字符串划分成尽量短的若干相同子串。
分析:
策略:
next [ i ] 表示 a [ 0 ] 到 a [ i - 1] 的最长相同前后缀长度。那么如果 n % ( n - next [ n ] ) == 0 ,则最短子串为 n - next [ n ] ;否则为 n。
该策略正确性证明:
首先证明若能被整除,那么原串一定可以划分为该子串:
设原串为:
则可说明:
即:
下面再证明:如果 n % ( n - next [ n ] ) != 0 ,则一定不存在长度小于n的满足要求的子串。
用反证法,假设存在一个满足要求的长度为 m 的子串(m < n 且 n%m = 0)且 n % ( n - next [ n ] ) ! = 0 。那么设 t = n - next [ n ] 。
显然:
这时就可以得到:
现在终于证明完毕该策略的正确性,然后理论上才能用。
代码:
#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;
}
}
后注:
在网上找了半天都没有找到一篇证明上述策略正确性的文章,不知道这我一篇会不会访问量比较高。