传送门:题目
题意:
给一个字符串,找到它的最短循环节,比如说ababab,那么最短循环节是ab,然后题目说有的循环节是残缺的,让我们求残缺部分的长度,比如说abcdab,循环节是abcd,然后ab部分残缺,残缺部分长度为2,我们输出2。注意:循环节至少有两个,比如说abcde,我们要输出5,补一个完整的循环节。
题解:
扩展KMP中的next可以帮助我们找到最短循环节,我们知道next的值代表当前前缀的上一个索引位置,对这句话不理解的可以参考这篇博客:KMP的next数据的含义
知道next数组的含义了,我们可以看下这个例子。
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
str | a | b | a | b | a | b | |
next | -1 | 0 | 0 | 1 | 2 | 3 | 4 |
str和next是要错一位的,不懂的可以看上面那篇博客
我们看index:6位值对应的str:b字母,它的next是4,那么代表当前前缀的上一个位置就是4。那么最短循环节就是6-4=2,然后再从index:6位置往前推2个长度,我们得到,最短循环节repetend=”ab”
我们再接受一种思想,就是既然长串是循环的,那么它的循环节不唯一。
比如说:abcda,正常思维,人脑cpu得出的答案是repetend=”abcd”,但是它的repetend还可以为”bcda”,也就是从后面往前取循环节,我们第一个例子就是模拟的这种思维,下一个例子更加明显。
下一个例子:
index | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
str | a | b | c | d | a | |
next | -1 | 0 | 0 | 0 | 0 | 1 |
我们按照上面的思路,index:5对应的str:a字母next为1,然后我们往前推,是不是得到了repetend=”bcda”,符合上面的思想。
AC代码:(求next数组套个板子就好了,就是正常kmp的板子)
#include <iostream>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 100010;
int KMP_next[maxn];
void kmp_pre(char x[], int m, int KMP_next[]) {//kmp未优化版
//如果多次使用KMP,不需要对next memset
//abab -> next={-1,0,0,1,2};
int i = 0, j = KMP_next[0] = -1;
while (i < m) {
while (j != -1 && x[i] != x[j])
j = KMP_next[j];
KMP_next[++i] = ++j;
}
}
int main(void) {
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--) {
char str[maxn];
cin>>str;
int len = strlen(str);
kmp_pre(str, len, KMP_next);
int repetend = len - KMP_next[len];//repetend代表最短循环节
if (len % repetend == 0 && len != repetend)
cout << 0 << endl;
else
cout << repetend - KMP_next[len] % repetend<<endl;
}
return 0;
}