码迷,mamicode.com
首页 > 编程语言 > 详细

【BZOJ1717】产奶的模式(后缀数组)

时间:2018-01-23 18:15:17      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:swa   code   coder   证明   长度   ems   def   check   org   

【BZOJ1717】产奶的模式(后缀数组)

题面

权限题
hihocoder
洛谷

题解

\(hihocoder\)里面讲的非常好了


这题要求的就是最长可重叠重复K次子串

所谓相同的子串
我们可以理解为如果有两个后缀的前缀相同
那么就有一个相同的子串

如果两个后缀的前缀相同
那么他们在\(SA\)中的排名是接近的

再说清楚点
如果两个后缀的前缀相同
必然是在后缀排序中一段连续的后缀都拥有这个相同的前缀

因此,求出\(height\)数组之后
考虑如何计算答案:
直接搞显然搞不出来
因此二分一下答案

如何\(check\)是否存在长度为\(mid\)\(K\)重复子串呢?
既然是一段连续的区间
因此,就需要检查是否有超过\(K\)个连续的\(height\)\(>=mid\)

这个很容易证明:
如果有\(l..r\)\(height\)都超过了\(mid\)
那么,证明这一段区间中任意两个后缀的
\(LCP\)长度都至少为\(mid\)
所以,此时这个\(mid\)一定出现了超过\(K\)

所以,大力二分一下即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 1200000
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int SA[MAX],Rank[MAX],x[MAX],y[MAX],t[MAX];
int height[MAX],a[MAX];
int n,K;
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void GetSA()
{
    int m=1000010;
    for(int i=1;i<=n;++i)t[x[i]=a[i]]++;
    for(int i=1;i<=m;++i)t[i]+=t[i-1];
    for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=1;i<=n;++i)y[i]=0;
        for(int i=n-k+1;i<=n;++i)y[++p]=i;
        for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
        for(int i=0;i<=m;++i)t[i]=0;
        for(int i=1;i<=n;++i)t[x[y[i]]]++;
        for(int i=1;i<=m;++i)t[i]+=t[i-1];
        for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
        swap(x,y);
        x[SA[1]]=p=1;
        for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
        if(p>=n)break;
        m=p;
    }
    for(int i=1;i<=n;++i)Rank[SA[i]]=i;
    for(int i=1,j=0;i<=n;++i)
    {
        if(j)j--;
        while(a[i+j]==a[SA[Rank[i]-1]+j])j++;
        height[Rank[i]]=j;
    }
}
bool check(int h)
{
    int cnt=0;
    for(int i=2;i<=n;++i)
    {
        if(height[i]<h)cnt=0;else cnt++;
        if(cnt==K-1)return true;
    }
    return false;
}
int main()
{
    n=read();K=read();
    for(int i=1;i<=n;++i)a[i]=read();
    GetSA();
    int l=1,r=n,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

【BZOJ1717】产奶的模式(后缀数组)

标签:swa   code   coder   证明   长度   ems   def   check   org   

原文地址:https://www.cnblogs.com/cjyyb/p/8336811.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!