zoukankan      html  css  js  c++  java
  • 洛谷P2852

    Portal

    Description

    给出一个(n(nleq2 imes10^4))个数的序列,求最长的出现了(k)次的子串的长度。

    Solution

    后缀数组+二分答案。
    二分子串长度(len),若(h[x..y]geq len)则说明有一个长度为(len)的子串,分别在(sa[x-1..y])出现过共(y-x+2)次。所以只要找出最长的(hgeq len)的区间,对于每个(len)从头到尾扫一遍即可找出这个区间。

    时间复杂度(O(nlogn))

    Code

    //[USACO06DEC]牛奶模式Milk Patterns
    #include <cstdio>
    #include <cstring>
    inline char gc()
    {
        static char now[1<<16],*s,*t;
        if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
        return *s++;
    }
    inline int read()
    {
        int x=0; char ch=gc();
        while(ch<'0'||'9'<ch) ch=gc();
        while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
        return x;
    }
    int max(int x,int y) {return x>y?x:y;}
    int const N=2e4+10;
    int n,m,s[N];
    int sa[N],rnk[N<<1],h[N];
    int cnt[N*50],tmp[N],rnk1[N<<1];
    void getSA()
    {
        for(int i=1;i<=n;i++) cnt[s[i]]=1;
        for(int i=1;i<=1e6;i++) cnt[i]+=cnt[i-1];
        for(int i=1;i<=n;i++) rnk[i]=cnt[s[i]];
        for(int L=1;L<=n;L<<=1)
        {
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[i+L]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) tmp[cnt[rnk[i+L]]--]=i;
            memset(cnt,0,sizeof cnt);
            for(int i=1;i<=n;i++) cnt[rnk[tmp[i]]]++;
            for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) sa[cnt[rnk[tmp[i]]]--]=tmp[i];
            int k=0; memcpy(rnk1,rnk,sizeof rnk);
            for(int i=1;i<=n;i++)
            {
                if(rnk1[sa[i]]!=rnk1[sa[i-1]]||rnk1[sa[i]+L]!=rnk1[sa[i-1]+L]) k++;
                rnk[sa[i]]=k;
            }
            if(k>=n) break;
        }
        for(int i=1,k=0;i<=n;i++)
        {
            if(rnk[i]==1) {h[1]=0; continue;}
            if(k) k--;
            while(s[i+k]==s[sa[rnk[i]-1]+k]) k++;
            h[rnk[i]]=k;
        }
    }
    int check(int x)
    {
        int res=0;
        for(int i=1,fr=1;i<=n+1;i++)
            if(h[i]<x) res=max(res,i-fr+1),fr=i+1;
        return res;
    }
    int main()
    {
        n=read(),m=read(); for(int i=1;i<=n;i++) s[i]=read();
        getSA();
        int L=1,R=n;
        while(L<=R)
        {
            int mid=L+R>>1;
            if(check(mid)>=m) L=mid+1;
            else R=mid-1;
        }
        printf("%d
    ",R);
        return 0;
    }
    

    P.S.

    注意在check(x)中我循环到了(n+1),否则统计不到处于末尾的(hgeq len)的区间。
    同权限题BZOJ1717

  • 相关阅读:
    [Leetcode Week3]Evaluate Division
    [Leetcode Week3]Course Schedule
    [Leetcode Week3]Clone Graph
    [Leetcode Week2]Sort List
    [Leetcode Week2]Sort Colors
    [Leetcode Week2]Merge Intervals
    [Leetcode Week1]Longest Substring Without Repeating Characters
    哈夫曼树的数组实现
    cocos2d-x3.16 default模式项目 Android Studio C++文件编译失败问题
    html display和visibility在资源加载上的区别
  • 原文地址:https://www.cnblogs.com/VisJiao/p/LgP2852.html
Copyright © 2011-2022 走看看