【題解】CF1063F String Journey(SA)

【題解】CF1063F String Journey(SA)

好久寫SA沒有1A了

考慮最優解的形式,顯然是\(k,k-1,k-2\dots 1\)

直覺告訴我們從後往前考慮,這樣就只用考慮\(k\)\(k+1\)的增量。設\(dp(i,j)=0/1\)且欽定第一個串是\(S(i,i+j-1)\)是否存在"Journey"。

轉移枚舉一個\(dp(k,j-1)\)然後判斷一下\(S(k,k+j-1+1)\)是否是\(S(i,i+j-1)\)的子串即可。注意到因爲長度差\(=1\),可以分別枚舉\(S(i,i+j-1)\)長度爲\(j-1\)的前綴和後綴判等就行。

容易發現固定\(i\)\(dp(i,j)=1\)\(j\)是一段前綴。那麼只要記最大的那個就行了,記爲\(dp(i)\)(和前面的定義共存)。

轉移要枚舉一個\(j\)複雜度還是\(\ge O(n^2)\),但我們還能發現,\(dp(i)\le dp(i+1)+1\)。那麼枚舉\(j\)的複雜度均攤到\(O(2n)\)

這個轉移可以用SA優化,因爲SA數組上可以轉移而來的(滿足子串限制的)是一段區間,用線段樹維護下\(dp(i)\)的最大值就行。

但是我們忽略了一個要求是\(k>t+j-1\)。本來很難處理,但由於這裏\(dp(i)\le dp(i+1)+1\),也就是說\(i+1\)合法的\(k\)恰好\(i\)也合法,而且我們從大到小枚舉的\(k\),所以加入過的\(dp(k)\)不需要撤銷,線段樹搞搞就行了。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;  typedef long long ll;
const int maxn=5e5+5;
char s[maxn];
int n,lg[maxn],st[20][maxn],sa[maxn],rk[maxn],dp[maxn],seg[maxn<<2];

void sufsort(char*str,int*sa,int*rk,int*h,int n){
	static int temp[maxn<<1],buk[maxn];
	for(int k=0,m=128;(1<<k>>1)<=n;++k){
		if(!k) for(int t=1;t<=n;++t) temp[t]=t,temp[t+n]=0,rk[t]=str[t];
		int l=1<<k>>1,p=0,q=l;
		for(int t=1;t<=n;++t){
			if(sa[t]>n-l) temp[++p]=sa[t];
			if(sa[t]>l) temp[++q]=sa[t]-l;
		}
		for(int t=1;t<=n;++t) ++buk[rk[t]];
		for(int t=1;t<=m;++t) buk[t]+=buk[t-1];
		for(int t=n;t;--t) sa[buk[rk[temp[t]]]--]=temp[t];
		memcpy(temp+1,rk+1,n<<2);  memset(buk+1,0,m<<2); rk[sa[1]]=1;
		for(int t=2;t<=n;++t)
			rk[sa[t]]=temp[sa[t-1]]==temp[sa[t]]&&temp[sa[t-1]+l]==temp[sa[t]+l]?rk[sa[t-1]]:rk[sa[t-1]]+1;
		m=rk[sa[n]];
	}
	for(int t=1,p=0;t<=n;++t){
		if(p) --p;
		if(rk[t]!=1) while(str[t+p]==str[sa[rk[t]-1]+p]) ++p;
		else p=0;
		h[rk[t]]=p;
	}
}

int que(int l,int r){
	return min(st[lg[r-l+1]][l],st[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);
}

#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
int getL(int pos,int v){
	if(st[0][pos]<v) return pos+1;
	int l=1,r=pos-1;
	do{
		if(que(mid,pos)>=v) r=mid-1;
		else l=mid+1;
	}while(l<=r);
	return l;
}

int getR(int pos,int v){
	if(st[0][pos+1]<v) return pos-1;
	int l=pos+1,r=n;
	do{
		if(que(pos+1,mid)>=v) l=mid+1;
		else r=mid-1;
	}while(l<=r);
	return r;
}

void upd(int p,int v,int l,int r,int pos){
	if(p<l||p>r) return;
	if(l==r) return seg[pos]=v,void();
	if(p<=mid) upd(p,v,lef);
	if(mid<p) upd(p,v,rgt);
	seg[pos]=max(seg[pos<<1],seg[pos<<1|1]);
}

int que(int L,int R,int l,int r,int pos){
	if(L>r||R<l) return 0;
	if(L<=l&&r<=R) return seg[pos];
	return max(que(L,R,lef),que(L,R,rgt));
}

#undef mid 
#undef lef
#undef rgt

int main(){
	cin>>n>>(s+1);
	sufsort(s,sa,rk,st[0],n);
	for(int t=2;t<=n;++t) lg[t]=lg[t>>1]+1;
	for(int t=1;t<=lg[n];++t)
		for(int i=1;i+(1<<t>>1)<=n;++i)
			st[t][i]=min(st[t-1][i],st[t-1][i+(1<<t>>1)]);
	int ans=dp[n]=1;
	for(int t=n,j=2;t;--t,++j){
		while(j){
			if(t+j<=n) upd(rk[t+j],dp[t+j],1,n,1);
			if(que(getL(rk[t],j-1)-1,getR(rk[t],j-1),1,n,1)>=j-1) break;
			if(que(getL(rk[t+1],j-1)-1,getR(rk[t+1],j-1),1,n,1)>=j-1) break;
			--j;
		}
		ans=max(dp[t]=j,ans);
	}
	cout<<ans<<endl;
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章