POJ1743 Musical Theme (後綴數組,不可重疊最長重複子串)
題目鏈接:傳送門
思路:
首先可以知道對於兩個相同旋律的拍子(即兩個拍子全部增加某個值後會相同),假設長度爲k,那麼拍子的後k-1個數與他們的前一個數的差都是相同的。
所以我們可以將數組轉化爲差數組,無效值,然後要求從差數組中找到長度最長的兩個子序列滿足,子序列不重疊且間隔爲至少1(如果這兩個子序列挨着的話,那麼對應的拍子就重疊了)
所以我們可以二分這個子序列的長度,對於每個長度,我們根據是否小於長度來進行分組,對於每個分組,如果這個組的下標最大值與最小值滿足,則這個解可以。
#include<algorithm>
#include<stdio.h>
#include<iostream>
#include<string.h>
#define mset(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int N=2e4+10;
int t1[N],t2[N],c1[N];//輔助數組
void SA(int *s,int n,int sa[],int rank[],int height[])//基數排序的版本中
{
int m=200;//桶的大小,會在下面循環中變化,第一關鍵詞r的最大值,初始時是字符的最大值,之後都<=n
int *sb=t1,*r=t2,*c=c1;//輔助數組,分別爲:基數排序所用的第二2,rak',cnt數組
//用基數排序求出長度爲1的sa[]和rank[],如果字符最大值較大,第一輪可以採用sort
for(int i=0; i<=m; ++i) c[i]=0;
for(int i=1; i<=n; ++i) c[r[i]=s[i]]++;
for(int i=1; i<=m; ++i) c[i]+=c[i-1];
for(int i=n; i>=1; --i) sa[c[s[i]]--]=i;
for(int k=1,p ; k < n; k<<=1 ) //p是一個計數器,現在還沒用。
{
//sb[i]:第二關鍵詞排名爲i的位置爲sb[i]
p=0;
for(int i=n-k+1; i<=n; ++i) sb[++p]=i;
for(int i=1; i<=n; ++i) if(sa[i]>k) sb[++p]=sa[i]-k;
//基數排序求出2k長度的sa數組
for(int i=0; i<=m; ++i) c[i]=0;
for(int i=1; i<=n; ++i) c[r[i]]++;
for(int i=1; i<=m; ++i) c[i]+=c[i-1];
for(int i=n; i>=1; --i) sa[ c[r[sb[i]]]-- ]=sb[i];
std::swap(r,sb);
//現在要利用上輪的r和這輪的sa求這輪的r
r[sa[1]]=p=1;
for(int i=2,a,b; i<=n; ++i)
{
a=sa[i],b=sa[i-1];
if(sb[a]==sb[b]&&(a+k<=n && b+k<=n&&sb[a+k]==sb[b+k]) ) r[a]=p;
else r[a]=++p;
}
if(p>=n) break;//可以提前退出
m=p;
}
/*計算高度數組*/
int k=0;
for(int i=1; i<=n; ++i) rank[sa[i]]=i;
height[1]=0;
for(int i=1; i<=n; ++i)
{
if(k) k--;
int j=sa[rank[i]-1];
if(j==0) continue;
while(s[j+k]==s[i+k]) ++k;
height[rank[i]]=k;
}
}
int rank[N],sa[N],height[N]; //height[1]的值爲0
int w[N],d[N];
/*
搞成差分數組 前綴相同長度至少爲4的不重疊
*/
bool judge(int v,int n)//在v數組中能否構成長度>=v的不重疊子串
{
//分成幾個不相交的區間,求每個區間的
int l=1,minn=sa[1],maxx=sa[1];
for(int i=2;i<=n;++i)
{
if(height[i]>=v) {
minn=std::min(minn,sa[i]);
maxx=std::max(maxx,sa[i]);
}
else{
if(maxx-minn>=v) return true;
else minn=sa[i],maxx=sa[i];
}
}
if(maxx-minn>=v+1) return true;
else return false;
}
int main()
{
int n;
while(scanf("%d",&n),n)
{
for(int i=1;i<=n;++i) scanf("%d",w+i),d[i]=w[i]-w[i-1]+100;
d[1]=0;
d[n+1]=-1;
SA(d,n,sa,rank,height);
int l=4,r=n;
int ans=-1;
while(l<=r)
{
int m=l+r>>1;
if(judge(m,n))
{
ans=m;
l=m+1;
}
else r=m-1;
}
if(ans==-1)
printf("0\n");
else
printf("%d\n",ans+1);
}
return 0;
}