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;
    }
    
    
    
  • 相关阅读:
    mysql 历史版本下载
    mysql 5.7 版本 You must reset your password using ALTER USER statement before executing this statement报错处理
    5.7 zip 版本的安装 以及遇到的坑
    mysql 5.6zip版本的卸载与5.7 zip 版本的安装
    mysql数据库的备份与还原
    本地Navicat连接docker里的mysql
    docker修改数据库密码
    docker 在push镜像到本地registry出现的500 Internal Server Error
    linux 没有界面内容显示不全解决办法
    json与map互相转换
  • 原文地址:https://www.cnblogs.com/winlere/p/13028035.html
Copyright © 2011-2022 走看看