poj 1743 [USACO5.1] Musical Theme (后缀数组+二分)

洛谷P2743传送门

题目大意:给你一个序列,求其中最长的一对相似等长子串

一对合法的相似子串被定义为:

1.任意一个子串长度都大于等于5

2.不能有重叠部分

3.其中一个子串可以在全部+/-某个值后和另一个串完全相同

还是老套路了,其实只要求出每一项和前一项的差值序列,这样第三个问题可以被转化为求最长不重叠的相同子串,最后把答案+1就行了

这道题竟然还是LTC大佬的男人八题!正解是后缀数组+二分,但据说可以n^2骗分过

思路是先求出height数组,对于排序后的字符串集合有一个神奇的性质,如果有某连续的几个字符串height>=x,那么这几个字符串的公共前缀长度>=x,可以用Trie树的思想很容易地证明

那现在就要解决第二个问题,不能有重叠部分

直接求解不容易,所以我们二分答案去做,每次二分出一个答案x,然后去height数组里找,把连续的几个height>=x字符串想象成一块,那么如果这个块合法,这一块内一定存在两个字符串的sa[i]-sa[j]>=x,也就是在这一块内找出sa[i]的最大最小值就行啦

复杂度O(nlogn)

傻了吧唧的把后缀数组敲错了,调了半个小时

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20010
#define inf 0x3f3f3f3f
#define maxn 100
using namespace std;

int n,len;
int a[N],b[N],tr[N],rk[N],sa[N],hs[N],h[N];
int gint()
{
    int rett=0,fh=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    while(c>='0'&&c<='9'){rett=(rett<<3)+(rett<<1)+c-'0';c=getchar();}
    return rett*fh;
}
void clr()
{
    memset(a,0,sizeof(a));memset(tr,0,sizeof(tr));
    memset(b,0,sizeof(b));memset(rk,0,sizeof(rk));
    memset(h,0,sizeof(h));memset(sa,0,sizeof(sa));
    memset(hs,0,sizeof(hs));
}
bool check(int k,int x,int y){
    if(x+k>len||y+k>len) return 0;
    else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?1:0;
}
void get_sa()
{
    int cnt=0,i=0;
    for(i=1;i<=n;i++) hs[b[i]]++;
    for(i=1;i<=2*maxn;i++) if(hs[i]) tr[i]=++cnt;
    for(i=1;i<=2*maxn;i++) hs[i]+=hs[i-1];
    for(i=1;i<=n;i++) rk[i]=tr[b[i]],sa[hs[b[i]]--]=i;
    for(int k=1;cnt<len;k<<=1)
    {
        for(i=1;i<=cnt;i++) hs[i]=0;
        for(i=1;i<=len;i++) hs[rk[i]]++;
        for(i=1;i<=cnt;i++) hs[i]+=hs[i-1];
        for(i=len;i>=1;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--;
        for(i=1;i<=k;i++) tr[len-i+1]=hs[rk[len-i+1]]--;
        for(i=1;i<=len;i++) sa[tr[i]]=i;
        for(i=1,cnt=0;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-1])?cnt:++cnt;
        for(i=1;i<=len;i++) rk[i]=tr[i];
    }
}
void get_height()
{
    for(int i=1;i<=len;i++){
        if(rk[i]==1) continue;
        for(int j=max(1,h[rk[i-1]]-1);j;j++)
            if(b[i+j-1]==b[sa[rk[i]-1]+j-1]) h[rk[i]]=j;
            else break;
    }
}
bool check(int ans)
{
    int i=1,j=1;
    for(i=1;i<=len;)
    {
        if(h[i]<ans) {i++;continue;}
        int mi=sa[i-1],ma=sa[i-1];
        for(;h[i]>=ans&&i<=len;i++)
        {
            mi=min(mi,sa[i]);
            ma=max(ma,sa[i]);
            if(ma-mi>ans) return 1;
        }
    }return 0;
}

int main()
{
    while(scanf("%d",&n)&&n!=0)
    {
        len=n;
        clr();
        for(int i=1;i<=n;i++)
            a[i]=gint(),b[i]=a[i]-a[i-1]+maxn;
        get_sa();
        get_height();
        int l=0,r=n,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        if(ans<4) printf("0\n");
        else printf("%d\n",ans+1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/guapisolo/article/details/82828478