poj1743 Musical Theme(后缀数组)

题目求的是差分后的最长不重复相同子串

利用后缀数组求解,首先答案具有二分性,也就是重复子串越大越好

因此先二分最长长度,根据最长长度将所有后缀分组按height数组分组

依据是height数组具有排名相邻的肯定是lcp最大的,而因此如果一段相邻的height数组的大小都大于等于二分值,这说明这段排名内的lcp都大于二分值

这是因为假设i和j排名在这一段内那么答案就是height数组区间取min,显然都是大于二分值。反之一旦遇到height小于二分值就断开。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
int s[N],px[N];
int n;
int sa[N],rk[N],od[N],id[N],cnt[N],h[N],st[N][25];
bool cmp(int x,int y,int w){
    return od[x]==od[y]&&od[x+w]==od[y+w];
}
int tmp[N];
bool check(int len) {
    int mx = sa[1], mi = sa[1];
    for(int i = 2; i <= n; i++) {
        if(h[i]>=len)
            mx=max(sa[i], mx),
            mi=min(sa[i], mi);
         else
            mx=mi=sa[i];
        if(mx-mi>len) return 1;
    }
    return 0;
}
int main(){
    //ios::sync_with_stdio(false);
    while(cin>>n){
        if(n==0)
        break;
        int i;
        for(int i=1;i<=n;i++){
            scanf("%d",&s[i]);
        }
        for(i=1;i<=n;i++)
            s[i]=s[i+1]-s[i]+90;
        int m=max(n,300);
        memset(cnt,0,sizeof cnt);
        memset(h,0,sizeof h);
        memset(sa,0,sizeof sa);
        memset(rk,0,sizeof rk);
        memset(id,0,sizeof id);
        for(i=1;i<=n;i++) cnt[rk[i]=s[i]]++;
        for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
        int p;
        for(int w=1,p=0;w<n;w<<=1,m=p){
            for(p=0,i=n;i>n-w;i--){
                id[++p]=i;
            }
            for(i=1;i<=n;i++){
                if(sa[i]>w){
                    id[++p]=sa[i]-w;
                }
            }
            memset(cnt,0,sizeof cnt);
            for(i=1;i<=n;i++) cnt[px[i]=rk[id[i]]]++;
            for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
            memcpy(od,rk,sizeof rk);
            for(p=0,i=1;i<=n;i++){
                rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
            }
        }
        for(i=1;i<=n;i++){
            if(rk[i]==1)
                continue;
            int a=max(h[rk[i-1]]-1,0);
            while(i+a<=n&&s[i+a]==s[sa[rk[i]-1]+a])
                a++;
            h[rk[i]]=a;
        }
        int l=0,r=n;
        while(l<r){
            int mid=l+r+1>>1;
            if(check(mid))
                l=mid;
            else
                r=mid-1;
        }
        if(l+1>=5){
            cout<<l+1<<endl;
        }
        else{
            cout<<0<<endl;
        }
    }
}
View Code

 

posted @ 2020-07-14 10:39  朝暮不思  阅读(139)  评论(0编辑  收藏  举报