一、题意

有kase组数据(kase<=200)。
每组数据一个字符串(长度<=1000),要求列出该字符串中,包含i个循环节的循环子串的最大长度。
输出列出的答案数组max_cir_len[i].

二、解析

循环节问题考虑使用KMP。
用KMP的Next数组只能求出前缀子串的循环节,而此题要求的是所有子串,因此需要枚举起始点。
考虑时间复杂度O(n^2),不会超时。

三、代码

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
const int maxn = 1000 + 5;
int kase, ans[maxn], Next[maxn];

void getNext(char* p, int m) {
    Next[0] = -1;
    int i = 0, j = -1;
    while(i < m) {
        if(j == -1 || p[i] == p[j]) i ++, j ++, Next[i] = j;
        else j = Next[j];
    }
}

int main() {
    cin >> kase;
    for(int k = 1; k <= kase; k ++) {
        string str;
        cin >> str;
        int n = str.size();
        fill(ans, ans + n + 1, 0);
        for(int i = 0; i < n; i ++) {
            int len = n - i;
            getNext(&str[i], len);
            for(int j = 1; j <= len; j ++) {
                int p = j;
                while(p) {
                    p = Next[p];
                    if(p && j % (j - p) == 0) {
                        int cir_num = j / (j - p);
                        ans[cir_num] = max(ans[cir_num], j);
                    }
                }
            }
        }
        ans[1] = n;
        cout << "Case #" << k << ":";
        for(int i = 1; i <= n; i ++) cout << " " << ans[i];
        cout << endl;
    }
}

四、归纳

  • 注意KMP算法算出来的循环节长度是最小循环节长度,如果需要算出所有可能循环节长度的话,就要用一个while循环一节一节往回跳+判定,由于往回跳的长度就是循环节的长度,因此实际上这是一个枚举循环节长度的过程。比如字符串aaaa,用KMP算法求出的循环节长度为1,但实际上2也是一个循环节长度,此时就要往回跳+判定来枚举全。

又WA了好久...