黑红兔
题意
题解
这暴力比正解还快3倍。
看到这道题,应该是很容易想到dp的。
令
d
p
i
,
j
dp_{i,j}
dpi,j表示选取区间
i
,
j
i,j
i,j时的最大段数,这样dp,明显是
O
(
n
4
)
O\left(n^4\right)
O(n4),只有
10
p
t
s
10pts
10pts。
我们明显是可以对上面的dp进行优化的。
很明显,一定存在一种选法只比上面一段多
1
1
1,所以总的段数长度一定不会超过
2
n
\sqrt{2n}
2n,可以将
d
p
dp
dp的第二维换成该段长度,也就被压缩到了
O
(
n
3
)
O\left(n^3\right)
O(n3)。
而如果像上面这样的话,
d
p
i
,
j
dp_{i,j}
dpi,j是一定等于
j
j
j的,于是第
2
2
2维就可以被去掉。我们直接对第一维进行枚举即可,有方程式
d
p
i
=
max
i
+
1
n
(
min
(
min
(
d
p
j
,
l
c
p
(
i
,
j
)
)
,
j
−
i
)
+
1
)
dp_{i}=\max_{i+1}^{n}(\min(\min(dp_{j},lcp(i,j)),j-i)+1)
dpi=maxi+1n(min(min(dpj,lcp(i,j)),j−i)+1)。
于是,我们可以在枚举
i
,
j
i,j
i,j的时候,对这两者的后面的
l
c
p
(
i
,
j
)
lcp(i,j)
lcp(i,j)值进行二分,找出它们的最长
l
c
p
lcp
lcp,比对两个串是否相等可以采用哈希。
有了上面的方法,我们就可以对我们的统计方法进行更深一步优化了。
如果我们的哈希模数开得足够小,我们就可以直接记录下出现了那些串了。
我们可以直接对看
i
i
i的
m
i
d
mid
mid长度的后缀是否出现过,这样就不需要枚举
j
j
j了。
时间复杂度
O
(
n
n
l
o
g
n
)
O\left(n\sqrt{n}log\,n\right)
O(nnlogn)。
但当串够长是可能会出现判错的情况,所以我们要尽量减少我们枚举的串数量。
很容易发现有结论
f
i
⩽
f
i
+
1
+
1
f_{i}\leqslant f_{i+1}+1
fi⩽fi+1+1,所以我们可以缩小我们的二分上界,使之判断数变少。
其实这样就可以过了,但是这明显不是正解嘛。
但事实上我们是不需要对
f
f
f进行二分的,上面
f
f
f的形式与
k
m
p
kmp
kmp的形式很像,可以用势能的形式考虑,我们真正枚举的
f
f
f次数总和是不会超过
n
n
n的。
所以,我们可以先采取后缀数组,使所有的后缀排序后,找到
l
c
p
(
i
,
j
)
⩽
m
i
d
lcp(i,j)\leqslant mid
lcp(i,j)⩽mid的那一段,这个过程可以用st表,rmq和二分来进行维护,找到区间,即可能贡献我们
f
f
f的
j
j
j必须
r
k
rk
rk在这个范围内。
之后再看这个区间中有没有点的dp值大于等于
m
i
d
mid
mid,这个可以通过主席树来进行维护。
主席树下标为
r
k
rk
rk,树上维护已有后缀
r
k
rk
rk在该节点区间内的
d
p
dp
dp最大值。
如果我们枚举
f
f
f所对应的后缀区间内有
d
p
j
⩾
f
−
1
dp_{j}\geqslant f-1
dpj⩾f−1的点,那么就能够贡献到我们的
d
p
i
dp_{i}
dpi。
由于我们的
j
j
j要求
j
⩾
i
+
f
i
j\geqslant i+f_{i}
j⩾i+fi,我们直接在
i
+
f
i
i+f_{i}
i+fi的主席树上查询我们
l
c
p
(
i
,
j
)
⩾
f
i
−
1
lcp(i,j)\geqslant f_i-1
lcp(i,j)⩾fi−1区间的最大值就行了。
说不定直接指针也行。
时间复杂度
O
(
n
l
o
g
n
)
O\left(nlog\,n\right)
O(nlogn),但实测跑得没有哈希快。
源码
正解
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<unordered_map>
using namespace std;
const int mo=1e8+7;
const int jzm=233;
const int INF=0x7f7f7f7f;
#define MAXN 500005
typedef long long LL;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
char str[MAXN];
int n,m,ans,dp[MAXN],sa[MAXN],rk[MAXN],hei[MAXN],st[25][MAXN];
int x[MAXN],y[MAXN],c[MAXN],z[MAXN],lg[MAXN],root[MAXN];
struct ming{int lson,rson,maxx;};
int add(const int x,const int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
void getSa(){
for(int i=1;i<=n;i++)c[x[i]=str[i]]++;
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>0;i--)sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=1;i<=m;i++)c[i]=0;
for(int i=1;i<=n;i++)c[x[i]]++,z[i]=x[i];
for(int i=2;i<=m;i++)c[i]+=c[i-1];
for(int i=n;i>0;i--)sa[c[x[y[i]]]--]=y[i];
for(int i=1;i<=n;i++)x[i]=y[i]=0;x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)x[sa[i]]=(z[sa[i]]==z[sa[i-1]]&&z[sa[i]+k]==z[sa[i-1]+k])?num:++num;
if(num==n)break;m=num;
}
}
void getHi(){
int k=0;for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1;i<=n;i++){
if(k)k--;int j=sa[rk[i]-1];
while(str[i+k]==str[j+k])k++;hei[rk[i]]=k;
}
}
void getSt(){
for(int j=2;j<=n;j++)lg[j]=lg[j>>1]+1;
for(int i=0;i<n;i++)st[0][i]=hei[i+1];
for(int i=1;i<=lg[n];i++)
for(int j=0;j<n-(1<<i-1);j++)
st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
int rmq(int l,int r){if(l==r)return INF;return min(st[lg[r-l]][l],st[lg[r-l]][r-(1<<lg[r-l])]);}
struct SegmentTree{
private:
int tot;ming tr[MAXN*30];
public:
void insert(int &now,int las,int l,int r,int ai,int aw){
now=++tot;tr[now]=tr[las];tr[now].maxx=max(tr[now].maxx,aw);
if(l==r)return ;int mid=l+r>>1;
if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai,aw);
if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai,aw);
}
int query(int rt,int l,int r,int al,int ar){
if(l>r||r<al||l>ar||!rt)return 0;int mid=l+r>>1,res=0;
if(al<=l&&r<=ar)return tr[rt].maxx;
if(al<=mid)res=max(res,query(tr[rt].lson,l,mid,al,ar));
if(ar>mid)res=max(res,query(tr[rt].rson,mid+1,r,al,ar));
return res;
}
}T;
bool check(int pos,int len,int R){
int l=rk[pos],r=n,al=l,ar=l;while(l<r){int mid=l+r+1>>1;if(rmq(rk[pos],mid)<len)r=mid-1;else l=mid;}
ar=l;l=1;r=rk[pos];while(l<r){int mid=l+r>>1;if(rmq(mid,rk[pos])<len)l=mid+1;else r=mid;}al=l;
return T.query(root[R],1,n,al,ar)>=len;
}
int main(){
freopen("brr.in","r",stdin);
freopen("brr.out","w",stdout);
scanf("%s",str+1);n=(int)strlen(str+1);m=126;getSa();getHi();getSt();
for(int i=n;i>0;i--){
dp[i]=1;
for(int j=min(n-i+1,dp[i+1]);j>=dp[i];j--)if(check(i,j,i+j+1)){dp[i]=j+1;break;}
for(int j=min(n-i,dp[i+2]);j>=dp[i];j--)if(check(i+1,j,i+j+1)){dp[i]=j+1;break;}
T.insert(root[i],root[i+1],1,n,rk[i],dp[i]);
}
for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
printf("%d\n",ans);
return 0;
}
哈希
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<unordered_map>
using namespace std;
const int mo=1e8+7;
const int jzm=233;
#define MAXN 500005
typedef long long LL;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
bool mh[mo+5];char str[MAXN];
int n,dp[MAXN],lim,hs[MAXN],ph[MAXN],phi[MAXN],ans,maxx;
int add(const int x,const int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
int Hash(const int l,const int r){return 1ll*add(hs[r],mo-hs[l-1])*phi[l-1]%mo;}
int main(){
freopen("brr.in","r",stdin);
freopen("brr.out","w",stdout);
scanf("%s",str+1);n=(int)strlen(str+1);ph[0]=phi[0]=1;const int inv=qkpow(jzm,mo-2);
for(int i=1;i<=n;i++)ph[i]=1ll*ph[i-1]*jzm%mo,phi[i]=1ll*phi[i-1]*inv%mo;
for(int i=1;i<=n;i++)hs[i]=add(hs[i-1],1ll*ph[i-1]*(str[i]-96)%mo);maxx=1;
int now=n+1,las=n+1;
for(int i=n;i>0;i--){
dp[i]=1;int l=0,r=min(n-i+1,min(maxx,dp[i+1]+1));
while(l<r){int mid=l+r+1>>1;if(mh[Hash(i,i+mid-1)])l=mid;else r=mid-1;}
dp[i]=max(dp[i],l+1);l=0,r=min(n-i+1,min(maxx,dp[i+1]+1));
while(l<r){int mid=l+r+1>>1;if(mh[Hash(i+1,i+mid)])l=mid;else r=mid-1;}
dp[i]=max(dp[i],l+1);now=i+dp[i];maxx=max(maxx,dp[i]);
for(int j=now;j<las;j++)for(int k=j;k<j+dp[j];k++)mh[Hash(j,k)]=1;las=now;
}
for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
printf("%d\n",ans);
return 0;
}