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;
    }
    
    
    
  • 相关阅读:
    bzoj4044/luoguP4762 [Cerc2014]Virus synthesis(回文自动机+dp)
    bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp)
    bzoj3926/luoguP3346 [Zjoi2015]诸神眷顾的幻想乡(trie上构建广义后缀自动机)
    bzoj3144 [HNOI2013]切糕(最小割)
    知识点简单总结——原根和指标
    uoj86 mx的组合数 (lucas定理+数位dp+原根与指标+NTT)
    rest_framework 学习笔记(一)
    Django 数据库操作
    02-Kubenetes资源
    10-Helm
  • 原文地址:https://www.cnblogs.com/winlere/p/13028035.html
Copyright © 2011-2022 走看看