【题解】2021CCPC华为云挑战赛 重叠的子串

2021CCPC华为云挑战赛 重叠的子串

题意

给定一个只含小写字母的字符串\(s\)和\(q\)次询问,每次询问给定一个字符串,以\(s[l...r]\)的形式给出,判断\(s\)中是否存在两个或多个出现的有重叠部分的给定子串。比如在“ababa”​中,两个 “aba” 子串就重叠于中间的字母 “a”,而两个 “ab” 子串就没有发生重叠。\(T\)组数据。

\(1\le n\le10^5,1\le q\le 10^6,1\le T\le 20,\sum n\le 6\times 10^5,\sum q\le 3\times 10^6\)

题解

对\(s\)建\(SAM\),用set+启发式合并维护\(parent\)树上每个节点的\(endpos\)集合,由于合并时只会向set中加入节点,所以容易维护每个节点\(u\)的\(endpos\)集合中任意两个元素的最小差值,记为\(ans[u]\),只需在插入时用当前元素和其前后两个元素的差值更新当前节点的答案。对于每个询问,先倍增跳到\(parent\)树上对应的节点\(u\)上,再比较串长和\(ans[u]\)即可。

#include <bits/stdc++.h>
#define pb(x) emplace_back(x)
using namespace std;
using P=pair<int,int>;
typedef long long ll;
const int N=200005,M=26;
char S[N];
int n,m,x,y;
vector<int> e[N];
int anc[N][21],ans[N],id[N];
set<int> s[N];
struct sam{
	int fa[N],len[N],lst,gt,ch[N][M],pos[N];
	void init(){gt=lst=1;}
	void init2(){
		for(int p=1;p<=gt;p++){
			id[p]=0;ans[p]=n+1;fa[p]=0;
			e[p].clear();
			s[p]=move(set<int>{});
			for(int i=0;i<M;i++)if(ch[p][i])ch[p][i]=0;
		}
		lst=gt=1;
	}
	void ins(int c,int id){
		int f=lst,p=++gt;lst=p;
		len[p]=len[f]+1;pos[p]=id;
		while(f&&!ch[f][c])ch[f][c]=p,f=fa[f];
		if(!f){fa[p]=1;return ;}
		int x=ch[f][c],y=++gt;
		if(len[x]==len[f]+1){gt--;fa[p]=x;return ;}
		len[y]=len[f]+1;fa[y]=fa[x];pos[y]=pos[x];fa[x]=fa[p]=y;
		for(int i=0;i<M;i++)ch[y][i]=ch[x][i];
		while(f&&ch[f][c]==x)ch[f][c]=y,f=fa[f];
	}
	void f1(){
		for(int u=gt;u>=1;u--){
			anc[u][0]=fa[u];
			e[fa[u]].pb(u);
		}
		dfs(1);
		id[0]=1;
		for(int i=1;i<=n;i++){
			int c=S[i]-'a';
			id[i]=ch[id[i-1]][c];
		}
	}
	void dfs(int u){
		int &tmp=ans[u];tmp=n+1;
		for(int i=1;i<=20;i++){
			anc[u][i]=anc[anc[u][i-1]][i-1];
		}
		for(auto v:e[u]){
			dfs(v);
			tmp=min(tmp,ans[v]);
			if(u==1){continue;}
			if(s[u].size()>=s[v].size()){
				for(auto j:s[v]){
					auto it=s[u].insert(j).first;
					if(it!=s[u].begin()){
						--it;
						tmp=min(tmp,j-*it);
						++it;
					}
					if((++it)!=s[u].end()){
						tmp=min(tmp,*it-j);
					}
				}
			}
			else{
				for(auto j:s[u]){
					auto it=s[v].insert(j).first;
					if(it!=s[v].begin()){
						--it;
						tmp=min(tmp,j-*it);
						++it;
					}
					if((++it)!=s[v].end()){
						tmp=min(tmp,*it-j);
					}
				}
				s[u]=move(s[v]);
			}
		}
	}
}g;
void f1(){
	int Q;
	scanf("%d%d%s",&n,&Q,S+1);
	g.init2();
	for(int i=1;i<=n;i++){
		g.ins(S[i]-'a',i);
		s[g.lst].insert(i);
	}
	g.f1();
	for(int i=1;i<=Q;i++){
		int x,y;scanf("%d%d",&x,&y);
		int p=id[y],l=y-x+1;
		for(int i=20;i>=0;i--){
			int na=anc[p][i];
			if(na>1&&g.len[na]>=l){
				p=na;
			}
		}
		if(ans[p]<l){
			printf("Yes\n");
		}
		else{
			printf("No\n");
		}
	}
}
int main(){
	int t;scanf("%d",&t);
	while(t--){
		f1();
	}
	return 0;
}
上一篇:2021CCPC中国大学生程序设计竞赛女生专场 K 音乐游戏


下一篇:2021CCPC网络选拔赛题解