伟大的 chy,请赐予我力量&chy 自动机的 SAM 做法

https://www.luogu.com.cn/problem/U260001

给定一组文本串,再每次给你一个询问串,求文本串的前缀集合中有多少串的 border 集合包含该询问串。

考虑 border 也许会往 kmp 方面入手,因为 kmp 能求出来一个前缀的最长 border。但注意到,这个包含意味着可能不是最长的,而不是最长的你对应位置又要发生变化,显然无法从最长 border 转移。

然而,你观察到一个串的 border 集合包含询问串,显然询问串是该串的前缀,且该串又作为后缀出现。

于是考虑对每个文本串的前缀求出其 endpos,其 endpos 位置的前缀的 border 集合一定包含该询问前缀。

至于去重,你考虑用 trie 对于出现过的前缀就不赋值,但询问的时候还是要考虑其贡献的。

对答案的记录直接挂在 trie 上即可。

#include <bits/stdc++.h>
//#define int long long
#define pb push_back
using namespace std;
const int N=(int)(5e6+5);
struct edge {
	int nex,to;
}e[N*3];
char s[N];
bool fl[N];
int las,tot=1,m,q,n,val[N*3],len[N*3],fa[N*3],cch[26][N],ctot=1,ch[26][N],sz[N*3],hea[N*3],cnt;

void add_edge(int x,int y) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; hea[x]=cnt;
}

//int ins(int pre,int c,bool ok) {
//	if(ch[c][pre]&&len[ch[c][pre]]==len[pre]+1) return ch[c][pre];
//	int x=++tot; bool chk=ch[c][pre]; len[x]=len[pre]+1; 
//	if(ok) sz[x]=1;
//	for(;pre&&!ch[c][pre];pre=fa[pre]) ch[c][pre]=x;
//	int y=ch[c][pre];
//	if(!pre) {
//		fa[x]=1; return x;
//	} else if(len[y]==len[pre]+1) {
//		fa[x]=y; return x;
//	}
//	int p=++tot; len[p]=len[pre]+1;
//	fa[p]=fa[y]; fa[x]=fa[y]=p;
//	for(int i=0;i<26;i++) ch[i][p]=ch[i][y];
//	for(;pre&&ch[c][pre]==y;pre=fa[pre]) ch[c][pre]=p;
//	if(chk) return p;
//	return x;
//}

void ins(int c,bool ok) {
	int x=++tot,pre=las; las=x; len[x]=len[pre]+1;
	sz[x]=ok;
	for(;pre&&!ch[c][pre];pre=fa[pre]) ch[c][pre]=x;
	int y=ch[c][pre];
	if(!pre) return fa[x]=1,void();
	else if(len[y]==len[pre]+1) return fa[x]=y,void();
	else {
		int p=++tot; len[p]=len[pre]+1;
		fa[p]=fa[y]; fa[y]=fa[x]=p;
		for(int i=0;i<26;i++) ch[i][p]=ch[i][y];
		for(;pre&&ch[c][pre]==y;pre=fa[pre]) ch[c][pre]=p;
	} 
}

void dfs(int x) {
//	cout<<x<<" "<<sz[x]<<'\n';
	for(int i=hea[x];i;i=e[i].nex) {
		int y=e[i].to;
		dfs(y); sz[x]+=sz[y];
	}
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>m>>q;
	while(m--) {
		cin>>s+1; n=strlen(s+1);
		las=tot=1; int p=1;
		for(int i=1;i<=n;i++) {
			bool ok=0; int c=s[i]-'a';
			if(!cch[c][p]) cch[c][p]=++ctot,ok=1;
			p=cch[c][p];
			ins(c,ok);
//			las=ins(las,s[i]-'a',ok);
			fl[i]=ok;
		}
		for(int i=2;i<=tot;i++) add_edge(fa[i],i);
		dfs(1);
		int tp=1; p=1;
		for(int i=1;i<=n;i++) {
			int c=s[i]-'a';
			tp=cch[c][tp]; p=ch[c][p];
			if(fl[i]) {
				if(sz[p]>=1) val[tp]+=sz[p]-1;
			} else {
				if(sz[p]) val[tp]+=sz[p];
			}
		}
		for(int i=1;i<=n;i++) fl[i]=0;
		for(int i=1;i<=tot;i++) {
			sz[i]=hea[i]=fa[i]=len[i]=0;
			for(int j=0;j<26;j++) ch[j][i]=0;
		}
		cnt=0; las=1; tot=1;
	}
	while(q--) {
		cin>>s+1; n=strlen(s+1);
		int p=1; bool ok=1;
		for(int i=1;i<=n;i++) {
			int c=s[i]-'a';
			if(!cch[c][p]) {
				ok=0; break ;
			}
			p=cch[c][p];
		}
		if(!ok) {
			cout<<"0\n"; continue ;
		}
		cout<<val[p]<<'\n';
	}
	return 0;
} 

posted @ 2022-11-10 19:11  FxorG  阅读(62)  评论(0编辑  收藏  举报