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; } } }
没有人不辛苦,只有人不喊疼