SPOJ 687:REPEATS——后缀数组+RMQ

题面

  洛谷

解析

  先奉上YYR的PPT

   

   基本思路这张PPT已经讲清楚了,但还有一些其他的细节

  我们确定了$L*t$与$L*(t+1)$后,显然不能暴力向后跳或向前跳。考虑向后跳最多跳$LCP(L*t, L*(t+1))$个点,因此这个可以用后缀数组+RMQ预处理出来,快速查询。再考虑向前跳,向前跳最多产生1个循环节,因为如果有多个循环节的话,必定存在一个更小的t使得向前跳跳到相同起点,并且最多产生一个循环节,那么什么时候会产生一个循环节呢?

  令$len = LCP(L*t, L*(t+1))$, 此时循环节个数为$res = len / L + 1$, 超出循环节的部分为$k' = len % L$,显然如果能向前跳至少$k = L - k'$个点,就可以产生1个循环节,即满足$LCP(L*t-k, L*t-k+L) geqslant L$时,$ res++$

 代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 50004;

int T, n, ans;
int sa[maxn], rk[maxn], fir[maxn], sec[maxn], c[maxn], hei[maxn];
char s[maxn];

void Build_SA()
{
    int m = 26;
    for(int i = 1; i <= n; ++i)
        fir[i] = s[i] - 'a' + 1;
    for(int i = 0; i <= m; ++i)
        c[i] = 0;
    for(int i = 1; i <= n; ++i)
        c[fir[i]] ++;
    for(int i = 1; i <= m; ++i)
        c[i] += c[i-1];
    for(int i = n; i; --i)
        sa[c[fir[i]]--] = i;
    for(int k = 1; k <= n; k <<= 1)
    {
        int t = 0;
        for(int i = n - k + 1; i <= n; ++i)
            sec[++t] = i;
        for(int i = 1; i <= n; ++i)
            if(sa[i] > k)
                sec[++t] = sa[i] - k;
        for(int i = 0; i <= m; ++i)
            c[i] = 0;
        for(int i = 1; i <= n; ++i)
            c[fir[sec[i]]] ++;
        for(int i = 1; i <= m; ++i)
            c[i] += c[i-1];
        for(int i = n; i; --i)
            sa[c[fir[sec[i]]]--] = sec[i], sec[i] = 0;
        for(int i = 1; i <= n; ++i)
            swap(fir[i], sec[i]);
        t = 0;
        fir[sa[1]] = ++t;
        for(int i = 2; i <= n; ++i)
            if(sec[sa[i]] != sec[sa[i-1]] || sec[sa[i]+k] != sec[sa[i-1]+k])
                fir[sa[i]] = ++t;
            else
                fir[sa[i]] = t;
        if(t >= n)
            break;
        m = t;
    }
    for(int i = 1; i <= n; ++i)
        fir[i] = sec[i] = 0;
}

void Get_hei()
{
    int h = 0;
    for(int i = 1; i <= n; ++i)
        rk[sa[i]] = i;
    for(int i = 1; i <= n; ++i)
    {
        int t = sa[rk[i]-1];
        while(s[t+h] == s[i+h])     h++;
        hei[rk[i]] = h;
        h = max(0, h - 1);
    }
}

int lg[maxn], mn[20][maxn];

void RMQ()
{
    for(int i = 1; i <= n; ++i)
        mn[0][i] = hei[i];
    for(int j = 1; j <= lg[n]; ++j)
        for(int i = 1; i + (1<<j) - 1 <= n; ++i)
            mn[j][i] = min(mn[j-1][i], mn[j-1][i+(1<<(j-1))]);
}

int query(int x, int y)
{
    if(x > y)    swap(x, y);
    return min(mn[lg[y-x]][x+1], mn[lg[y-x]][y-(1<<lg[y-x])+1]);
}

int main()
{
    lg[0] = -1;
    for(int i = 1; i <= 50000; ++i)
        lg[i] = lg[i>>1] + 1;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        char c[5];
        for(int i = 1; i <= n; ++i)
        {
            scanf("%s", c);
            s[i] = c[0];
        }
        s[n+1] = 0;
        Build_SA();
        Get_hei();
        RMQ();
        ans = 1;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j + i <= n; j += i)
            {
                int l = query(rk[j], rk[j+i]);
                int res = l / i + 1, k = i - l % i;
                if(j - k && query(rk[j-k], rk[j-k+i]) >= i)
                    res++;
                ans = max(ans, res);
                j += l / i * i;
            }
        printf("%d
", ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Joker-Yza/p/11348702.html