题意
每组给出一个长度为 n 的字符串, 要求输出这个字符串每个前缀为 i 时的最短循环节, 换句话说就是遍历这个字符串, 输出 i 和 长度为 i 的时候子串循环的次数
题解
如果 i%(i-next[i])==0 那么就有循环
循环次数为 i/(i-next[i])
循环长度为 i-next[i]
例如:aabaabaabaab
当 i=2 时,前缀为 aa,是由于 a 重复两次得到的,故有 k=2
当 i=6 时,前缀为 aabaab,是由于 aab 重复两次得到的,故有 k=2
当 i=9 时,前缀为 aabaabaab,是由于 a 重复三次得到的,故有 k=3
当 i=12 时,前缀为 aabaabaabaab ,是由于 a 重复四次得到的,故有 k=4
思路:
本质上还是求最小循环节长度,使用 KMP 的 next 数组即可解决,对于每组数据的字符串,枚举前缀 i ,当 i%(i-next[i])=0 时即存在循环节,其个数 k=i/(i-next[i])
代码
#include <bits/stdc++.h>
using namespace std;
#define rg register
#define sc scanf
#define pf printf
const int MAXN = 1e6+100;
char s[MAXN];
int nxt[MAXN];
void GET ( int len ) {
nxt[0] = -1;
rg int j = 0, k = -1;
while ( j < len ) {
if ( k==-1 || s[j]==s[k] ) nxt[++j] = ++k;
else k = nxt[k];
}
// for ( int i = 0; i < len; ++i ) cout <<nxt[i] << " " ; cout << endl;
}
int main ( ) { // freopen( "F:\\in\\E_Period.txt" , "r" , stdin ) ;
int cas = 0;
int len;
while ( ~sc( "%d ", &len ) && len ) {
sc( "%s ", s ); // cout << len << " " << s << endl ;
pf( "Test case #%d\n", ++cas );
GET( len );
rg int pl;
for ( rg int i = 2; i <= len; ++i ) {
pl = i - nxt[i];
if ( i!=pl && i%pl==0 ) {
pf( "%d %d\n", i, i/pl );
}
}
puts( "" );
}
return 0;
}
/* 求从 2 到 len 截取串, 每次输出 截取的长度 和 最小循环节次数 */
/* 如果i%(i-next[i])==0 那么就有循环
循环次数为 i/(i-next[i])
循环长度为 i-next[i] */
参考链接https://blog.csdn.net/u011815404/article/details/87950963