1. 程式人生 > >[POJ1743]Musical Theme

[POJ1743]Musical Theme

spa 字符串排序 前綴 關系 print ans fix ++ operator

題意:給一個數字串,求不可重疊的相似子串,兩個子串$a,b$相似的定義是$a_i-b_i$都相等

昨晚二爺講課,去膜拜一發,學了一下不知道學了多少次也沒懂的後綴數組

後綴數組能將字符串$S$的所有後綴$S_{i\cdots n}=\text{Suffix}(i)$排序,$sa_i$表示排第$i$名的後綴的開頭下標,$rank_i$表示$\text{Suffix}(i)$的排名,$height_i$表示排第$i$名的後綴和排第$i-1$名的後綴的最長公共前綴長度

考慮用倍增求$rank_i$,一開始把所有長度為$1$的字符串排序,然後排序長度為$2$的字符串,以此類推

假設當前要排所有長度為$L$的字符串,因為我們已經知道了所有長度為$\dfrac L2$的字符串的相對大小,所以我們把每個長度為$L$的字符串看成由兩個長度為$\dfrac L2$的字符串拼起來的東西,相當於用(長度為$\dfrac L2$的字符串的相對排名)構成的二元組來表示每一個長度為$L$的字符串

技術分享圖片

原理很簡單,就是用長度為$\dfrac L2$的字符串之間的關系得出長度為$L$之間的字符串的關系

容易看出當$L=1$時字符串大小就是字母大小,之後每次用上一次得出的$rank$組成二元組並排序

註意到我們是對$rank$組成的二元組排序,這意味著被排序的數$\leq n$,所以我們可以使用計數排序,代碼挺好寫的

int cnt[N],tmp[N];
void sort(int*x){
	int i,m=0;
	for(i=1;i<=n;i++){
		m=max(m,x[i]);
		cnt[x[i]]++;
	}
	for(i=1;i<=m;i++)x[i]+=x[i-1];
	for(i=n;i>0;i--)tmp[cnt[x[i]]--]=x[i];
	for(i=1;i<=n;i++)x[i]=tmp[i];
}

計數排序的原理是統計“有多少個數小於等於這個數”,這裏的自減符號用於處理元素相同的情況,倒過來處理保證了相等元素之間的相對順序不會改變(每處理完一個元素,下次碰到這個元素時它的下標已經減去了$1$)

對二元組的排序也容易解決,先對第二維排序,再對第一維排序(因為計數排序是穩定排序,當排完後第一維相等的連續段的第二維是有序的)

於是我們成功求出了$rank_i$,那麽$sa_i$也易於求得

我們還要求$height_i$,有一個關於它的結論是$height_{rank_i}\geq height_{rank_{i-1}}-1$,證明如下(來自許智磊的論文)

以下證明比較繞,請熟悉$sa,rank,height$的概念再看證明

當$height_{rank_{i-1}}\leq1$,顯然成立

當$height_{rank_{i-1}}\geq2$,先證一堆奇奇怪怪的東西

設$lcp(i,j)=lcp(\text{Suffix}(sa_i),\text{Suffix}(sa_j))(i\leq j)$,它等價於$lcp(\text{Suffix}({sa_{i\cdots j}}))$

因為更多串取最長公共前綴不會讓答案變大,所以如果$k\geq j\geq i$,那麽$lcp(k,j)\geq lcp(k,i)$

設$k=sa_{rank_{i-1}-1}$,即$\text{Suffix}(k)$是$\text{Suffix}(i-1)$的前一位

根據$lcp(\text{Suffix}(k),\text{Suffix}(i-1))=height_{rank_{i-1}}\geq2$,兩邊同時向右偏移$1$位,最長公共前綴$-1$,所以$lcp(\text{Suffix}(k+1),\text{Suffix}(i))=height_{rank_{i-1}}-1$

因為$rank_{k}\lt rank_{i-1}$且$height_{rank_{i-1}}\geq2$,所以不等式兩邊所代表的後綴第一位相同,右移一位不影響比較結果,推出$rank_{k+1}\lt rank_i$,也就是$rank_{k+1}\leq rank_i-1$

因為$rank_i\geq rank_i-1\geq rank_{k+1}$,所以$lcp(rank_i,rank_i-1)\geq lcp(rank_i,rank_{k+1})=lcp(\text{Suffix}(i),\text{Suffix}(k+1))=height_{rank_{i-1}}-1$

最後,$height_{rank_i}=lcp(rank_i,rank_i-1)\geq height_{rank_{i-1}}-1$,於是就證完了

如果按$height_{rank_{1\cdots n}}$這樣的順序來求,那麽它每次先$-1$,然後就只可能增加,這保證了整個求$height$的過程是$O(n)$的

算$height_i$的代碼一定要記好,考場上沒時間來證這種鬼定理

所以我們求出了後綴數組中用處最大的東西:$height_i$,下面用它做題

題目說“相似”,假如對原序列差分,那麽問題就變成了不可重疊最長重復子串

考慮二分,假設當前二分的答案是$k$,如果存在$[l,r]$使任意$i\in[l,r]$都有$height_i\geq k$,那麽這些後綴的$lcp$長度就$\geq k$,我們可以貪心地選取兩個相距最遠的後綴,也就是說如果$\max\left\{sa_{l\cdots r}\right\}-\min\left\{sa_{l\cdots r}\right\}\geq k$,那麽就存在兩個不重疊的長度為$k$的子串

因為差分了,所以最後答案應該$+1$

於是這題就做完了,唉終於填了一個看上去不可填的坑...

為什麽我寫的後綴數組常數這麽鬼大>_<

#include<stdio.h>
#include<string.h>
int s[100010],rk[200010],sa[100010],cnt[100010],id[100010],height[100010],n;
struct pr{
	int c[2],id;
	pr(int a=0,int b=0,int d=0){c[0]=a;c[1]=b;id=d;}
}p[100010],q[100010];
bool operator!=(pr a,pr b){return(a.c[0]!=b.c[0])||(a.c[1]!=b.c[1]);}
int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
void sort(int f){
	int i,m;
	memset(cnt,0,sizeof(cnt));
	m=0;
	for(i=1;i<=n;i++){
		cnt[p[i].c[f]]++;
		m=max(m,p[i].c[f]);
	}
	for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
	for(i=n;i>0;i--)q[cnt[p[i].c[f]]--]=p[i];
	for(i=1;i<=n;i++)p[i]=q[i];
}
void suf(){
	int i,l,m;
	memset(rk,0,sizeof(rk));
	for(i=1;i<=n;i++)rk[i]=s[i];
	for(l=1;l<=n;l<<=1){
		for(i=1;i<=n;i++)p[i]=pr(rk[i],rk[i+l],i);
		sort(1);
		sort(0);
		m=0;
		for(i=1;i<=n;i++){
			if(p[i]!=p[i-1])m++;
			rk[p[i].id]=m;
		}
	}
	for(i=1;i<=n;i++)sa[rk[i]]=i;
	l=0;
	for(i=1;i<=n;i++){
		if(l)l--;
		while(s[i+l]==s[sa[rk[i]-1]+l])l++;
		height[rk[i]]=l;
	}
}
bool check(int k){
	int i,mx,mn;
	for(i=1;i<=n;i++){
		if(height[i]>=k){
			mx=max(mx,max(sa[i-1],sa[i]));
			mn=min(mn,min(sa[i-1],sa[i]));
		}else{
			mx=-1;
			mn=n;
		}
		if(mx-mn>=k)return 1;
	}
	return 0;
}
int main(){
	int i,l,r,mid,ans;
	while(1){
		scanf("%d",&n);
		if(n==0)break;
		for(i=1;i<=n;i++)scanf("%d",s+i);
		n--;
		for(i=1;i<=n;i++)s[i]=s[i+1]-s[i]+88;
		suf();
		l=1;
		r=n>>1;
		ans=0;
		while(l<=r){
			mid=(l+r)>>1;
			if(check(mid)){
				ans=mid;
				l=mid+1;
			}else
				r=mid-1;
		}
		printf("%d\n",(ans<4)?0:(ans+1));
	}
}

[POJ1743]Musical Theme