题目链接 专题十六 KMP - F - Power Strings POJ - 2406
题意
给你一个字符串,让你找到一个子序列a,使字符串由a多次循环拼接得到。求循环的最多次数,及求a的最小长度。
思路一
我刚开始没有什么思路,后来想通过分解质因子来入手,毕竟这个字符串如果是由a多次拼接得到的,那么它一定是a.length的倍数。Next的数组还是一定要求的,里面存了一个最大前后缀,一定会对我的求解有帮助的。但是,不知为什么,得到的代码一直都WA。于是我开始了别的规律寻找。。。。。。
思路二
处于对Next数组的好奇,我开始对其进行探索,发现了一个规律,如果它符号题目条件,那么最后的一些数据必定会处于d为1的递增序列。而且最后位的Next必定是整个函数中最大(严格)的元素。
这样就很简单了,我用代码简单实现了这个规律后就AC了。但是Next数组远远没有这么简单。。。。。。
思路三
next数组关于循环节几个小应用
(1)i - next[i] 最小循环节(第一个字母开始)
(2)next[i] 最大循环节中的第几位数(此时循环节可交叉)
(3)next[i] != 0 && i % (i - next[i]) == 0,当前是循环节中的最 后一位.
(4)在(3)的前提下 i / (i - next[i]) 表示的最大周期个数,也就是在最小循环节的前提下的最大周期个数。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = (int)1e6+7;
const int INF = (int)0x3f3f3f3f;
int Next[MAXN];
char sub[MAXN];
int len;
void MakeNext(){
Next[0] = 0;
for (int i = 1,k = 0;i < len;i ++){
while (k > 0 && sub[i] != sub[k])
k = Next[k-1];
if (sub[i] == sub[k]) k ++;
Next[i] = k;
}
}
int main()
{
ios::sync_with_stdio(false);//scanf("%s",sub)
while (cin >> sub,sub[0] != '.'){
len = strlen(sub);
MakeNext();
int mn = -INF;
int ans = 0;
for (int i = 0;i < len;i ++){
if (Next[i] > mn){
ans = 0;
mn = Next[i];
}else if (Next[i] == mn){
ans ++;
}
}
if (mn == Next[len-1] && ans == 0) {
int L = len-Next[len-1];
if (len%L == 0)cout << len/L << endl; //printf("%d\n",len/L); //
else cout << 1 << endl; //printf("1\n"); //
}else {
cout << 1 << endl; //printf("1\n"); //
}
}
}
大神代码 根据思路三
#include <stdio.h>
#include <string.h>
char s[1000005];
int num[1000005];
int main()
{
int n,case1=1;
while(scanf("%s",s),s[0]!='.')
{
int j=-1;
num[0]=-1;
n=strlen(s);
for(int i=0;i<n;)
{
if(j==-1||s[i]==s[j])num[++i]=++j;
else j=num[j];
}
if(n%(n-num[n])==0)
printf("%d\n",n/(n-num[n]));
else puts("1");
}
return 0;
}