读书人

POJ 3693 反复次数最多的连续重复子串

发布时间: 2012-10-05 15:34:34 作者: rapoo

POJ 3693 重复次数最多的连续重复子串 后缀数组

题目大意就是求重复次数最多的连续重复子串。例如abababc 答案就是ababab 因为ab连续出现的次数最多

并且题目还要求输出字典序最小的

比如abababcdcdcd

ababab和cdcdcd都符合要求

但是ababab字典序小


具体做法参见罗穗骞的论文

穷举子串的长度L,然后求长度为L的子串最多出现几次

首先连续出现一次是肯定的,所以只考虑出现两次及以上的情况

假设在字符串中出现了两次,记这个重复了两次L长度子串的子串为S。

那么S肯定包含了字符r[0], r[L], r[L*2], r[3 * L]....中的某相邻的两个。

所以就看r[L*i]和r[L*(i + 1)]往前往后分别匹配到多远,记这个长度为K(具体匹配方式看代码),那么就连续出现了K/L+1次,最后看最大值多少

注意每次求这个k要分为两种情况,一是公共前缀恰好模L等于0,另一种是模L不等于0

模L不等于0时还要计算一个值,假如公共前缀%L等于t,就求lcp(i - (L - t), i - (L - t) + L);

为什么呢,我们画一画就知道了,这样的做法,实际上两个公共前缀往前延伸了几个位置,使得前缀的长度加上延伸的长度是L的倍数

然后求lcp,会发现,他是有可能比原来的k大的,那么连续出现的次数也有可能改变。所以这种情况不能遗漏

这里用到了lcp,既求任意两个后缀的最长公共前缀,使用RMQ实现。

最后输出的时候,由于要按字典序输出,就枚举sa数组


#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <cstdio>#include <cmath>#include <queue>#include <map>#include <set>#define MAXN 111111#define MAXM 200005#define INF 1000000011#define lch(x) x<<1#define rch(x) x<<1|1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define eps 1e-7using namespace std;int r[MAXN];int wa[MAXN], wb[MAXN], wv[MAXN], tmp[MAXN];int sa[MAXN]; //index range 1~n value range 0~n-1int cmp(int *r, int a, int b, int l){    return r[a] == r[b] && r[a + l] == r[b + l];}void da(int *r, int *sa, int n, int m){    int i, j, p, *x = wa, *y = wb, *ws = tmp;    for (i = 0; i < m; i++) ws[i] = 0;    for (i = 0; i < n; i++) ws[x[i] = r[i]]++;    for (i = 1; i < m; i++) ws[i] += ws[i - 1];    for (i = n - 1; i >= 0; i--) sa[--ws[x[i]]] = i;    for (j = 1, p = 1; p < n; j *= 2, m = p)    {        for (p = 0, i = n - j; i < n; i++) y[p++] = i;        for (i = 0; i < n; i++)            if (sa[i] >= j) y[p++] = sa[i] - j;        for (i = 0; i < n; i++) wv[i] = x[y[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--) sa[--ws[wv[i]]] = y[i];        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; i++)            x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;    }}int rank[MAXN]; //index range 0~n-1 value range 1~nint height[MAXN]; //index from 1   (height[1] = 0)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 Log[MAXN];int mi[MAXN][20];void rmqinit(int n){    for(int i = 1; i <= n; i++) mi[i][0] = height[i];    int m = Log[n];    for(int i = 1; i <= m; i++)        for(int j = 1; j <= n; j++)        {            mi[j][i] = mi[j][i - 1];            if(j + (1 << (i - 1)) <= n) mi[j][i] = min(mi[j][i], mi[j + (1 << (i - 1))][i - 1]);        }}int lcp(int a, int b){//询问a,b后缀的最长公共前缀    a = rank[a];    b = rank[b];    if(a > b) swap(a,b);    a ++;    int t = Log[b - a + 1];    return min(mi[a][t] , mi[b - (1<<t) + 1][t]);}char s[MAXN];int ans[MAXN];char out[MAXN];int main(){    Log[1] = 0;    for(int i = 2; i < MAXN; i++) Log[i] = Log[i >> 1] + 1;    int cas = 0;    while(scanf("%s", s) != EOF && strcmp(s, "#") != 0)    {        int n = strlen(s), m = 0;        for(int i = 0; i < n; i++)        {            m = max(m, (int)s[i]);            r[i] = s[i];        }        r[n] = 0;        da(r, sa, n + 1, m + 1);        calheight(r, sa, n);        rmqinit(n);        int mx = -1;        int cnt, l;        for(l = 1; l < n; l++) //枚举子串的长度        {            for(int i = 0; i + l < n; i += l)            {                int k = lcp(i, i + l);//计算r[L*i]和r[L*(i+1)]的最长公共前缀                int p = k / l + 1;                int t = l - k % l;                t = i - t;                //printf("ss i :%d i + l :%d t :%d t + l:%d p: %d\n", i, i + l, t, t + l, p);                if (t >= 0 && k % l != 0)                 {                    int tk = lcp(t, t + l);                    if(tk / l + 1 > p) p = tk / l + 1;                }                if(p > mx)                {                    cnt = 0;                    mx = p;                    ans[cnt++] = l;                }                if(p == mx) ans[cnt++] = l;            }        }        int pos = 0;        int flag = 0;        for(int i = 1; i <= n && !flag; i++)        {            for(int j = 0; j < cnt; j++)            {                int k = ans[j];                if(lcp(sa[i], sa[i] + k) >= (mx - 1) * k)                {                    pos = sa[i];                    l = mx * k;                    flag = 1;                    break;                }            }        }        printf("Case %d: ", ++cas);        for(int i = 0; i < l; i++) printf("%c", s[pos + i]);        printf("\n");    }    return 0;}


读书人网 >编程

热点推荐