【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;
}
posted @ 2021-04-21 20:43  cjTQX  阅读(60)  评论(0编辑  收藏  举报