zoukankan      html  css  js  c++  java
  • 【题解】CF1063F String Journey(SA)

    【题解】CF1063F String Journey(SA)

    好久写SA没有1A了

    考虑最优解的形式,显然是(k,k-1,k-2dots 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;
    }
    
    
    
  • 相关阅读:
    PAT B1045 快速排序 (25 分)
    PAT B1042 字符统计 (20 分)
    PAT B1040 有几个PAT (25 分)
    PAT B1035 插入与归并 (25 分)
    PAT B1034 有理数四则运算 (20 分)
    PAT B1033 旧键盘打字 (20 分)
    HDU 1231 最大连续子序列
    HDU 1166 敌兵布阵
    HDU 1715 大菲波数
    HDU 1016 Prime Ring Problem
  • 原文地址:https://www.cnblogs.com/winlere/p/13028035.html
Copyright © 2011-2022 走看看