后缀数组 Milk Patterns POJ - 3261

有n个数字,求有k个重复的最大长度(可重叠
先是WA,因为我就是把height数组从1开始扫了一遍,看是否大于或等于mid,如果是的话,就num++;这样的话,就不一定是同一个子串了,也就是说,我前几个最大前缀满足mid ,然后下一个不满足,然后下下个height【i】>=mid,这个时候就不是和前面同一个子串 了。所以是连续的height值。
然后改过了之后TLE。因为如果height[i]>=mid, 然后我就往后扫,然后找到一个不满足的,下一次我就从这个不满足的地方开始扫,而不是从第一个满足的后面开始扫。
然后过了,但是时间是200+ms,看了看别人都是100内,然后加了个离散化,终于79ms

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include<string>
using namespace std;
const int  maxn =2e4+10;
int vis[maxn];
int q[maxn];
struct Suffix
{
    int s[maxn];
    int SA[maxn],ranked[maxn],height[maxn];
    int x[maxn],y[maxn],buc[maxn],len,m;
    void init(int *str,int lenn)
    {
       // len=strlen(str);
       len=lenn;
        for(int i=0;i<len;i++) s[i]=(int)str[i];
        m=2e4+10;//
    }
    void GetSA()
    {
        for (int i = 0; i < m; i++)
            buc[i] = 0; // buc 是一个桶
        for (int i = 0; i < len; i++) buc[x[i] = s[i]]++;
        for (int i = 1; i < m; i++)
             buc[i] += buc[i - 1];
        for (int i = len - 1; i >= 0; i--)
            SA[--buc[x[i]]] = i;
        for (int k = 1; k <= len; k <<= 1) { // k 倍增
          int p = 0;
          //对第二关键字排序,y[i]:第i大的第二关键字是谁
          // 后缀 len - k 及之后的所有后缀第二关键字最小。为0
          for (int i = len - 1; i >= len - k; i--)
            y[p++] = i;
          for (int i = 0; i < len; i++)
                if (SA[i] >= k)
                    y[p++] = SA[i] - k;
         //总体来排个序,求出SA
          for (int i = 0; i < m; i++) buc[i] = 0;
          for (int i = 0; i < len; i++)
                buc[x[y[i]]]++;
          for (int i = 1; i < m; i++)
                buc[i] += buc[i - 1];
          for (int i = len - 1; i >= 0; i--)
                SA[--buc[x[y[i]]]] = y[i];
          swap(x, y);
          p = 1; x[SA[0]] = 0;
          // 重新计算每个一元的名次。则x数组里存的是总体的顺序
          for (int i = 1; i < len; i++) {
            if (y[SA[i - 1]] == y[SA[i]] && y[SA[i - 1] + k] == y[SA[i] + k])
              x[SA[i]] = p - 1;
            else x[SA[i]] = p++;
          }
          if (p >= len) break; // 每个后缀的名次已经完全不同,不需要继续倍增
              m = p; // 更新名次的最大值。
        }
    }
    void Getheight()
    {
        for(int i=0;i<len;i++)
            ranked[SA[i]]=i;
        int k=0;
        for(int i=0;i<len;i++)
        {
            if(ranked[i]==0) {height[0]=0;continue;}
            if(k) k--;
            int j=SA[ranked[i]-1];
            while(s[i+k]==s[j+k]&&i+k<len&&j+k<len) k++;
            height[ranked[i]]=k;
        }
    }
};
Suffix S;
int a[maxn];
int n,k;
int ok(int mid)
{
    int i=1;
    while(1)
    {
        int num=1;
        for(;i<=n-k+1;i++)
        {
            if(S.height[i]>=mid)
            {
                break;
            }
        }
        if(i>(n-k+1)) break;
        for(;i<=n;i++)
        {
            if(S.height[i]>=mid) num++;
            else break;
        }
        if(num>=k) return 1;
    }
    return 0;
}
int main()
{
    scanf("%d %d",&n,&k);
    for(int i=0;i<n;i++) {scanf("%d",&a[i]);q[i]=a[i];}
    sort(q,q+n);
    int nn=unique(q,q+n)-q;
    for(int i=0;i<n;i++)
    {
        int gg=lower_bound(q,q+nn,a[i])-q;
        a[i]=gg;
    }
    S.init(a,n);
    S.GetSA();
    S.Getheight();
    int l=1,r=n,res=1;
    while(l<=r)
    {
        int mid=l+(r-l)/2;
        if(ok(mid)) {
            res=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d\n",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值