题目大意:定义字符串的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的前缀”相同的缘故。
a | b | a | b | a | b | \0 |
-1 | 0 | 0 | 1 | 2 | 3 | 4 |
怎样从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;
}