hdu 1358 & hdu 3746 & poj 2406 & uva 12012 循环节与kmp

来源:互联网 发布:平板 win10 知乎 2017 编辑:程序博客网 时间:2024/06/16 13:07

参考

kmp next函数 kmp的周期问题,深入了解kmp中next的原理 ——Because Of You

HDU 1358

题意

对于给定的字符串 T,对其每一个前缀,问其是否由若干个循环节祖成。

思路

充要条件:len%(lenfail[len])==0

Code

#include <bits/stdc++.h>#define maxn 1000010using namespace std;int f[maxn], n;char s[maxn];typedef long long LL;void getfail(char* P) {    int m = strlen(P);    f[0] = f[1] = 0;    for (int i = 1; i < m; ++i) {        int j = f[i];        while (j && P[j] != P[i]) j = f[j];        f[i+1] = P[i] == P[j] ? j+1 : 0;    }}int kas;void work() {    scanf("%s", s);    getfail(s);    printf("Test case #%d\n", ++kas);    for (int i = 2; i <= n; ++i) {        if (f[i] && i % (i-f[i]) == 0) printf("%d %d\n", i, i / (i-f[i]));    }    printf("\n");}int main() {    while (scanf("%d", &n) != EOF && n) work();    return 0;}

HDU 3746

题意

对给定的串 T,在其尾部补上最少的字符使得它成为一个由若干个循环节组成的串。

思路

若本身就是(判定条件同上题),则补 0

否则找到开头处最小的(可能的)循环节,即 lenf[len].

Code

#include <bits/stdc++.h>#define maxn 100010using namespace std;char s[maxn];int f[maxn];typedef long long LL;void getfail() {    int n = strlen(s);    f[0] = f[1] = 0;    for (int i = 1; i < n; ++i) {        int j = f[i];        while (j && s[i] != s[j]) j = f[j];        f[i+1] = s[i] == s[j] ? j+1 : 0;    }}void work() {    scanf("%s", s);    getfail();    int n = strlen(s);    int diff = n - f[n];    if (n % diff == 0 && n / diff > 1) printf("0\n");    else printf("%d\n", diff - n % diff);}int main() {    int T;    scanf("%d", &T);    while (T--) work();    return 0;}

POJ 2406

题意

对于给定的字符串 T,输出它由最多多少个循环节组成。

思路

注意一下,如果不整除那么答案就是 1,就只有它本身一个循环节。

Code

#include <cstdio>#include <cstring>#define maxn 1000010using namespace std;typedef long long LL;char s[maxn];int f[maxn], n;void getfail() {    f[0] = f[1] = 0;    for (int i = 1; i < n; ++i) {        int j = f[i];        while (j && s[i] != s[j]) j = f[j];        f[i+1] = s[i] == s[j] ? j+1 : 0;    }}void work() {    n = strlen(s);    getfail();    if (n % (n - f[n]) == 0) printf("%d\n", n / (n - f[n]));    else printf("1\n");}int main() {    while (scanf("%s", s) != EOF && s[0] != '.') work();    return 0;}

UVa 12012

题意

对于给定的字符串 T,长度为 m,问对于所有的 l1lm),由 l 个循环节组成的最长的子串长度。

思路

枚举起点,对于每一个起点开始的子串求一次 fail 数组,然后直接用即可。

另外,若一个串能表示成 l 个循环节组成,那也一定能表示为 kk | l)个循环节组成,所以再枚举一下 l 的因子更新其答案。

Code

#include <bits/stdc++.h>#define maxn 1010using namespace std;typedef long long LL;char s[maxn];int f[maxn], a[maxn], m, kas;void getfail(char* P, int l) {    f[0] = f[1] = 0;    for (int i = 1; i < l; ++i) {        int j = f[i];        while (j && P[i] != P[j]) j = f[j];        f[i+1] = P[i] == P[j] ? j+1 : 0;    }}void work() {    memset(a, 0, sizeof(a));    scanf("%s", s);    m = strlen(s);    for (int i = 0; i < m; ++i) {        getfail(s+i, m-i);        for (int j = 1; j <= m-i; ++j) {            int circ = j - f[j];            if (j % circ) continue;            int cnt = j / circ;            for (int k = 1; k * k <= cnt; ++k) {                if (cnt % k == 0) a[cnt / k] = max(a[cnt / k], j), a[k] = max(a[k], j);            }        }    }    a[1] = m;    printf("Case #%d:", ++kas);    for (int i = 1; i <= m; ++i) printf(" %d", a[i]); printf("\n");}int main() {    int T;    scanf("%d", &T);    while (T--) work();    return 0;}