【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_2dots t_ku_{k+1}),且 (k) 最大。求最大的 (k)(|S|le 5 imes 10^5)

Solution

这题正解复杂度是 (mathcal O(nlog(n))),但这里我介绍一种 (mathcal O(nsqrt{n})) 且实际速度远快于前者的算法。

首先答案一定不超过 (mathcal O(sqrt{n})),考虑求出 (f_i) 表示当前答案的 (t_1)(i) 作为开头时的最大答案。

从后往前枚举,注意到 (f_ile 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
",ret);
	return 0;
}
原文地址:https://www.cnblogs.com/tqxboomzero/p/14686852.html