POJ 2406 Power Strings KMP 或 后缀数组

来源:互联网 发布:nginx 更改根目录 编辑:程序博客网 时间:2024/06/15 02:41

题目大意:

就是给出一个串S 长度不超过10^6, 求最大周期使得S = a^n也就是S是有n个字符串a连接起来的,求最大的n(也就是找到最短的a即可)


大致思路:

首先利用KMP的next数组可以知道循环节的个数, 为n/(n - next[n]) n是S的长度, 这个感觉还是有点晕...

另外一个做法是使用后缀数组


KMP的做法:

代码如下:

Result  :  Accepted     Memory  :  5024 KB     Time  :  141 ms

/* * Author: Gatevin * Created Time:  2015/2/2 15:07:55 * File Name: Iris_Freyja.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;/* * 首先如果S = a^n由next数组定义next[n]的值满足S[0~next[n] - 1] == S[(n - next[n]) ~ (n - 1)] * 如果设S1 = a^(n - 1)则S = S1*a那么在n处失配时由于由next数组的定义转到S1的结尾位置后一位进行匹配 * S[0~next[n] - 1] == S[(n - next[n]) ~ (n - 1)] = S1 * 那么n - next[n]刚好是循环节的长度, 且n % (n - next[n]) == 0 * 有种证明说不清楚的感觉....但是当n % (n - next[n]) == 0 的时候的确很好证明循环节是n - next[n] * 可是n % (n - next[n]) != 0的时候为什么没有循环节这个还没证明出来... */char s[1000010];int next[1000010];int main(){    while(scanf("%s", s))    {        if(strlen(s) == 1 && s[0] == '.') break;        memset(next, 0, sizeof(next));        int n = strlen(s);        for(int i = 1; i < n; i++)        {            int j = i;            while(j > 0)            {                j = next[j];                if(s[i] == s[j])                {                    next[i + 1] = j + 1;                    break;                }            }        }        if(n % (n - next[n]) == 0)            printf("%d\n", n/(n - next[n]));        else            printf("1\n");    }    return 0;}



另外是后缀数组的做法:

复杂虽然是O(n)但是常数看来很大= =

思路见代码注释, 另外求后缀数组的时候用倍增算法超时了..要用DC3算法

Result  :  Accepted     Memory  :  45508 KB     Time  :  2750 ms

/* * Author: Gatevin * Created Time:  2015/2/2 19:21:53 * File Name: Iris_Freyja.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;#define maxn 1000233#define F(x) ((x)/3 + ((x) % 3 == 1 ? 0 : tb))#define G(x) ((x) < tb ? (x)*3 + 1 : ((x) - tb)*3 + 2)/* * DC3算法求后缀数组...倍增算法会超时.. */int wa[maxn], wb[maxn], wv[maxn], Ws[maxn];int c0(int *r, int a, int b){    return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];}int c12(int k, int *r, int a, int b){    if(k == 2) return r[a] < r[b] || (r[a] == r[b] && c12(1, r, a + 1, b + 1));    else return r[a] < r[b] || (r[a] == r[b]  && wv[a + 1] < wv[b + 1]);}void sort(int* r, int *a, int *b, int n, int m){    int i;    for(i = 0; i < n; i++) wv[i] = r[a[i]];    for(i = 0; i < m; i++) Ws[i] = 0;    for(i = 0; i < n; i++) Ws[wv[i]]++;    for(i = 1; i < m; i++) Ws[i] += Ws[i - 1];    for(i = n - 1; i >= 0; i--) b[--Ws[wv[i]]] = a[i];    return;}void dc3(int *r, int *sa, int n, int m){    int i, j, *rn = r + n, *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;    r[n] = r[n + 1] = 0;    for(i = 0; i < n; i++) if(i % 3 != 0) wa[tbc++] = i;    sort(r + 2, wa, wb, tbc, m);    sort(r + 1, wb, wa, tbc, m);    sort(r, wa, wb, tbc, m);    for(p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)        rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;    if(p < tbc) dc3(rn, san, tbc, p);    else for(i = 0; i < tbc; i++) san[rn[i]] = i;    for(i = 0; i < tbc; i++) if(san[i] < tb) wb[ta++] = san[i] * 3;    if(n % 3 == 1) wb[ta++] = n - 1;    sort(r, wb, wa, ta, m);    for(i = 0; i < tbc; i++) wv[wb[i] = G(san[i])] = i;    for(i = 0, j = 0, p = 0; i < ta && j < tbc; p++)        sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];    for(; i < ta; p++) sa[p] = wa[i++];    for(; j < tbc; p++) sa[p] = wb[j++];    return;}int rank[maxn], height[maxn];void calheight(int* r, int* sa, int n){    int i, j, k = 0;    for(i = 1; i <= n; i++) rank[sa[i]] = i;    for(i = 0; i < n; height[rank[i++]] = k)        for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++);    return;}int s[3*maxn];char ts[maxn];int sa[3*maxn];int lcp[maxn];/* * 如果我们暴力枚举S的前k个字符会是一个循环节, * 那么Suffix(0)(从s[0]开始的后缀)与Suffix(k)会具有LCP值是n - k的关系 * 如果n % k == 0且LCP(rank[0], rank[k]) == n - k, 则前k个字符是S的循环节(可证明) * 而由LCP Theorem可知对于i < j 有 LCP(i, j) = min{LCP(k - 1, k) | i + 1 <= k < j} * 因此如果用lcp[i]表示LCP(rank[0], i)的话 * 利用height数组有height[i] = LCP(i, i - 1) * 则对于i < rank[0] lcp[i] = min(height[i + 1], lcp[i + 1]) * 对于i > rank[0] lcp[i] = min(height[i], lcp[i - 1]) * 递推求出lcp数组即可(这里因为rank[0]固定所以没有必要使用RMQ的查询问题, 预处理更简单) * 这样枚举长度k从1到n/2, 如果存在lcp[ran[k]] = n - k 即LCP(rank[0], rank[k])的话, k是满足条件的循环节 * 找出最小的这样的k即可 */int main(){    while(scanf("%s", ts))    {        int n = strlen(ts);        if(n == 1 && ts[0] == '.') break;        for(int i = 0; i < n; i++) s[i] = ts[i];        s[n] = 0;        dc3(s, sa, n + 1, 260);//可打印字符单位不会超过ASCII码表= =        calheight(s, sa, n);        //递推求解lcp[i] = LCP(rank[0], i)        int t = rank[0];        lcp[t - 1] = height[t];        lcp[t + 1] = height[t + 1];        for(int i = t - 2; i >= 0; i--) lcp[i] = min(height[i + 1], lcp[i + 1]);        for(int i = t + 2; i <= n; i++) lcp[i] = min(height[i], lcp[i - 1]);                for(int k = 1; k <= (n >> 1); k++)        {            if(n % k == 0 && lcp[rank[k]] == n - k)//寻找满足条件的最小的k            {                printf("%d\n", n/k);                goto nex;            }        }        printf("1\n");        nex:;    }    return 0;}


0 0
原创粉丝点击