CF1063F String Journey
http://codeforces.com/problemset/problem/1063/F
题解
首先我们注意到对于任意一种划分,我们都可以把它调整长度为\((len,len-1,len-2....1)\)的。所以我们可以用\((\tt{第一个串的位置,第一个串的长度})\)来表示一个划分。
所以我们可以设\(dp[i][j]\)表示\((i,j)\)这种方案是否可行,转移是从后往前的,可以用哈希,因为第二维是根号级别的,所以复杂度为\(n\sqrt{n}\)。
发现若有\(dp[i][j]\),那么就有\(dp[i][j-1]\),我们可以换一种状态设计令\(dp[i]\)表示最大的合法的\(j\)。
若要判断\((i,j)\)是否合法,我们需要在\((i+j,n)\)中找到\(lcp(i,x)\geq j-1 ||lcp(i+1,x)\geq j-1\),这个可以用后缀数组+主席树在\(logn\)的时间内判断。
这个东西可以二分,但是我们发现\(dp[i]\leq dp[i+1]+1\),于是我们的判断次数就变成\(O(n)\)的了。
代码
#include<bits/stdc++.h>
#define N 500009
#define inf 1e9
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
char s[N];
int n,tong[N],y[N],rnk[N],sa[N],dp[N],p[21][N],h[N],m,Log[N],tott,T[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
struct seg{
int l,r,mx;
}tr[N*21];
int query(int cnt,int l,int r,int L,int R){
if(!cnt)return 0;
if(l>=L&&r<=R)return tr[cnt].mx;
int mid=(l+r)>>1,ans=0;
if(mid>=L)ans=max(ans,query(ls,l,mid,L,R));
if(mid<R)ans=max(ans,query(rs,mid+1,r,L,R));
return ans;
}
void upd(int &cnt,int pre,int l,int r,int x,int y){
cnt=++tott;tr[cnt]=tr[pre];
if(l==r){
tr[cnt].mx=y;
return;
}
int mid=(l+r)>>1;
if(mid>=x)upd(ls,tr[pre].l,l,mid,x,y);
else upd(rs,tr[pre].r,mid+1,r,x,y);
tr[cnt].mx=max(tr[ls].mx,tr[rs].mx);
}
inline int lcp(int l,int r){
if(l>r)swap(l,r);
if(l==r)return inf;
l++;
int lo=Log[r-l+1];
return min(p[lo][l],p[lo][r-(1<<lo)+1]);
}
inline void qsort(){
for(int i=0;i<=m;++i)tong[i]=0;
for(int i=1;i<=n;++i)tong[rnk[i]]++;
for(int i=1;i<=m;++i)tong[i]+=tong[i-1];
for(int i=n;i>=1;--i)sa[tong[rnk[y[i]]]--]=y[i];
}
inline void SA(){
m=200;
for(int i=1;i<=n;++i)rnk[i]=s[i],y[i]=i;
qsort();
for(int w=1,p=0;p<n;w<<=1,m=p){
p=0;
for(int i=n-w+1;i<=n;++i)y[++p]=i;
for(int i=1;i<=n;++i)if(sa[i]>w)y[++p]=sa[i]-w;
qsort();
swap(rnk,y);
rnk[sa[1]]=p=1;
for(int i=2;i<=n;++i)
rnk[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:++p;
}
for(int i=1;i<=n;++i){
if(rnk[i]==1)continue;
int y=max(0,h[rnk[i-1]]-1);
while(s[i+y]==s[sa[rnk[i]-1]+y])y++;
h[rnk[i]]=p[0][rnk[i]]=y;
}
for(int i=1;(1<<i)<=n;++i)
for(int j=1;j+(1<<i)-1<=n;++j)
p[i][j]=min(p[i-1][j],p[i-1][j+(1<<i-1)]);
}
inline bool pd(int x,int len){
int l=rnk[x],r=n,ansr=0;
while(l<=r){
int mid=(l+r)>>1;
if(lcp(rnk[x],mid)>=len-1)ansr=mid,l=mid+1;
else r=mid-1;
}
l=1;r=rnk[x];
int ansl=n+1;
while(l<=r){
int mid=(l+r)>>1;
if(lcp(mid,rnk[x])>=len-1)ansl=mid,r=mid-1;
else l=mid+1;
}
if(ansl<=ansr)if(query(T[x+len],1,n,ansl,ansr)>=len-1)return 1;
x++;
l=rnk[x],r=n,ansr=0;
while(l<=r){
int mid=(l+r)>>1;
if(lcp(rnk[x],mid)>=len-1)ansr=mid,l=mid+1;
else r=mid-1;
}
l=1;r=rnk[x];
ansl=n+1;
while(l<=r){
int mid=(l+r)>>1;
if(lcp(mid,rnk[x])>=len-1)ansl=mid,r=mid-1;
else l=mid+1;
}
if(ansl<=ansr)if(query(T[x+len-1],1,n,ansl,ansr)>=len-1)return 1;
return 0;
}
int main(){
n=rd();
scanf("%s",s+1);
for(int i=2;i<=n;++i)Log[i]=Log[i>>1]+1;
SA();
int ans=0;
for(int i=n;i>=1;--i){
int mx=dp[i+1]+1;
while(!pd(i,mx))mx--;
dp[i]=mx;
upd(T[i],T[i+1],1,n,rnk[i],mx);
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}