HDU 3746 KMP 一维字符串找循环节

传送门:题目

题意:

给一个字符串,找到它的最短循环节,比如说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;
}

猜你喜欢

转载自blog.csdn.net/shadandeajian/article/details/81586896
今日推荐