POJ3261_Milk Patterns_后缀数组::求可重叠的k次最长重复子串

题意

给一个字符串,求其中至少重复了 k 次的最长子串的长度。

思路

后缀数组的学习资料:
https://wenku.baidu.com/view/228caa45b307e87101f696a8.html

题目链接

http://poj.org/problem?id=3261

AC代码

#include<cstdio>
#include<iostream>

using namespace std;

const int maxn = 20010;

int s[maxn];

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

void build_sa(int s[], int n, int m)
{
    int *x = t1, *y = t2;
                                                                                        //第一次基数排序
    for(int i= 0; i< m; i++) c[i] = 0;
    for(int i= 0; i< n; i++) c[x[i] = s[i]] ++;
    for(int i= 1; i< m; i++) c[i] += c[i - 1];
    for(int i= n-1; i>= 0; i--) sa[--c[x[i]]] = i;
                                                                                        //以后的n次基数排序
    for(int j= 1; j<= n; j<<= 1)
    {
        int p = 0;
        for(int i= n-j; i< n; i++) y[p++] = i;                                          //对第二关键字排序(利用前一次的sa)
        for(int i= 0; i< n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;

        for(int i= 0; i< m; i++) c[i] = 0;                                              //对第一关键字基数排序
        for(int i= 0; i< n; i++) c[x[y[i]]] ++;
        for(int i= 1; i< m; i++) c[i] += c[i-1];
        for(int i= n-1; i>= 0; i--) sa[--c[x[y[i]]]] = y[i];

        swap(x, y);                                                                     //本来保存在x中的上一次的rank转移到y中
        p = 1, x[sa[0]] = 0;                                                            //求rank
        for(int 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;                                                               //说明每一个后缀都有自己的rank,没有大小不分的了,排序结束
        m = p;                                                                          //更新最大值
    }
}

void getHeight(int s[], int n)                                                          //求height(利用 height[i] >= height[i-1] - 1
{
    int k = 0;
    for(int i= 0; i<= n; i++) rank[sa[i]] = i;
    for(int i= 0; i< n; i++)
    {
        if(k) k --;
        int j = sa[rank[i] - 1];
        while(s[i + k] == s[j + k]) k ++;
        height[rank[i]] = k;
    }
}

bool check(int n, int k, int t)
{
    int num = 1;
    for(int i= 2; i<= n; i++)
    {
        if(height[i] >= t)
        {
            num ++;
            if(num >= k) return true;
        }
        else num = 1;
    }

    return false;
}

int main()
{
    int n, k;
    while(scanf("%d %d", &n, &k) == 2)
    {
        int Max = 0;
        for(int i= 0; i< n; i++)
        {
            scanf("%d", &s[i]);
            Max = max(Max, s[i]);
        }

        s[n] = 0;                                                                       //注意
        build_sa(s, n+1, Max+1);
        getHeight(s, n);

        int l = 0, r = n;
        int ans = 0;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(check(n, k, mid))
            {
                ans = mid;
                l = mid + 1;
            }
            else r = mid - 1;
        }

        printf("%d\n", ans);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值