POJ 3693 连续重复子串

题目链接:http://poj.org/problem?id=3693

题意:给你一个字符串,让你求重复次数最多的连续重复子串

思路:先穷举长度L,然后求长度为L的子串最多能连续出现几次。首先,肯定能连续出现1次,所以我们直接考虑出现2次及以上的情况。假设在原字符串中连续出现了两次,记这个子字符串为S,那么S肯定包括了字符r[0], r[L], r[2*L], r[3*L], ..... 中相邻的某两个。所以,我们只需看字符r[L*i] 和 r[L*(i+1)] 往期和往后各能匹配多远,记这个总长度为K,那么这里连续出现了 K/L+1次。最后看最大值是多少。如图:

但是这道题还要用RMQ预处理,外加一个反向处理优化,看的bin神的讲解:

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1e5+5;

int sa[maxn], t1[maxn], t2[maxn], c[maxn];
int ranks[maxn], height[maxn];

void build_sa(int s[], int n, int m)
{
    int i, j, p, *x = t1, *y = t2;
    for (i = 0; i < m; ++i) c[i] = 0;
    for (i = 0; i < n; ++i) c[x[i] = s[i]]++;
    for (i = 1; i < m; ++i) c[i] += c[i-1];
    for (i = n-1; i >= 0; i--)  sa[--c[x[i]]] = i;
    for (j = 1; j <= n; j <<= 1) {
        p = 0;
        for (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 < m; ++i) c[i] = 0;
        for (i = 0; i < n; ++i) c[x[y[i]]]++;
        for (i = 1; i < m; ++i) c[i] += c[i-1];
        for (i = n-1; i >= 0; i--)  sa[--c[x[y[i]]]] = y[i];
        swap(x, y);
        p = 1;
        x[sa[0]] = 0;
        for (i = 1; i < n; ++i)
            x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ? p-1 : p++;
        if (p >= n) break;
        m = p;
    }
}

void getHeight(int s[], int n)
{
    int i, j, k = 0;
    for (i = 0; i <= n; ++i)    ranks[sa[i]] = i;
    for (i = 0; i < n; ++i) {
        if(k)   k--;
        j = sa[ranks[i]-1];
        while (s[i+k] == s[j+k])    k++;
        height[ranks[i]] = k;
    }
}

int mm[maxn];
int best[20][maxn];

void RMQ(int n)
{
    mm[0] = -1;
    for (int i = 1; i <= n; ++i)
        mm[i] = ((i & (i-1)) == 0) ? mm[i-1]+1 : mm[i-1];
    for (int i = 1; i <= n; ++i)    best[0][i] = i;
    for (int i = 1; i <= mm[n]; ++i) {
        for (int j = 1; j+(1<<i)-1 <= n; ++j) {
            int a = best[i-1][j];
            int b = best[i-1][j+(1<<(i-1))];
            if(height[a] < height[b])   best[i][j] = a;
            else    best[i][j] = b;
        }
    }
}

int askRMQ(int a, int b)
{
    int t;
    t = mm[b-a+1];
    b -= (1 << t)-1;
    a = best[t][a];
    b = best[t][b];
    return height[a] < height[b] ? a : b;
}

int lcp(int a, int b)
{
    a = ranks[a];
    b = ranks[b];
    if(a > b)   swap(a, b);
    return height[askRMQ(a+1, b)];
}

char str[maxn];
int r[maxn], a[maxn];
int main()
{
    int cas = 0;
    while (~scanf("%s", str)) {
        if(strcmp(str, "#") == 0) break;
        cas++;
        int n = strlen(str);
        for (int i = 0; i <= n; ++i)    r[i] = str[i];
        build_sa(r, n+1, 128);
        getHeight(r, n);
        RMQ(n);
        int cnt = 0, maxs = 0;
        for (int j = 1; j < n; ++j) {
            for (int i = 0; i + j < n; i+=j) {
                int t1 = lcp(i, i+j);
                int step = t1/j + 1;
                int k = i - (j-t1 % j);
                if(k >= 0 && t1 % j){
                    if(lcp(k, k+j) >= t1)   step++;
                }
                if(step > maxs){
                    maxs = step;
                    cnt = 0;
                    a[cnt++] = j;
                }
                else if(step == maxs)   a[cnt++] = j;
            }
        }
        int len = -1, st;
        for (int i = 1; i <= n && len == -1; ++i) {
            for (int j = 0; j < cnt; ++j) {
                int l = a[j];
                if(lcp(sa[i], sa[i]+l) >= (maxs-1)*l){
                    len = l;
                    st = sa[i];
                    break;
                }
            }
        }
        str[st+len*maxs] = 0;
        printf("Case %d: %s\n", cas, str+st);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值