【2021.4.21 校测/CF1063F】String Journey【哈希】
Description
给定字符串 \(S\),构造有序非空字符串 \(t_1,t_2,\dots t_k\)和可以为空的字符串 \(u_1,u_2,\dots u_{k+1}\),使 \(S=u_1t_1u_2t_2\dots t_ku_{k+1}\),且 \(k\) 最大。求最大的 \(k\)。\(|S|\le 5\times 10^5\)。
Solution
这题正解复杂度是 \(\mathcal O(nlog(n))\),但这里我介绍一种 \(\mathcal O(n\sqrt{n})\) 且实际速度远快于前者的算法。
首先答案一定不超过 \(\mathcal O(\sqrt{n})\),考虑求出 \(f_i\) 表示当前答案的 \(t_1\) 以 \(i\) 作为开头时的最大答案。
从后往前枚举,注意到 \(f_i\le f_{i+1}+1\),因此从 \(f_{i+1}+1\) 开始从大到小枚举 \(len\) 进行判断,当前长度 \(len\) 合法当且仅当 \(s[i,i+len-2]\)或者 \(s[i+1,i+len-1]\) 在 \(i+len-1\) 后出现过且是一个合法的开始串,因此我们用哈希表动态维护所有合法开始串即可,对于在 \(i+len-1\) 后出现这个限制,因为 \(i+len-1\) 是单调不增的,所以在每次 \(len\) 减少时加入原 \(i+len-1\) 开头的合法子串。
Code
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define mp make_pair
const ull base=233;
const int N=2e6+10;
int n,ans[N];
char s[N];
unordered_map<ull,int> pd;
ull hsh[N],pw[N];
inline void insert(int l,int r){pd[hsh[r]-hsh[l-1]*pw[r-l+1]]=1;}
inline bool query(int l,int len){int r=l+len-1;return pd.count(hsh[r]-hsh[l-1]*pw[r-l+1]);}
int main(){
scanf("%d",&n);
scanf("%s",s+1);
hsh[0]=0;pw[0]=1;
for(int i=1;i<=n;++i) pw[i]=pw[i-1]*base;
for(int i=1;i<=n;++i) hsh[i]=(hsh[i-1]*base)+s[i]-'a'+1;
insert(n,n-1);
int ret=0;
for(int l=n,len=0;l;--l){
len++;
while(!query(l,len-1)&&!query(l+1,len-1)){
len--;int r=l+len;
for(int i=ans[r];i;--i){
ull hs=hsh[r+i-1]-hsh[r-1]*pw[i];
if(pd.count(hs)) break;pd[hs]=1;
}
}
ans[l]=len;
ret=max(ret,ans[l]);
}
printf("%d\n",ret);
return 0;
}