• 题意:给n个字符串,问每个字符串在所有串里出现几次。
  • 思路:判断一个整串与其它串之间的包含关系通常用AC自动机。fail树是我们想象中的树,但是它可以帮助我们更好的理解AC自动机。
    1.fail树上的每个前缀都对应fail树里面的节点。通常节点存cnt[]表示有多少个串经过该点(即多少个前缀)
    2.fail树中祖先节点是子孙节点的后缀,其中父亲是最长严格后缀。反过来,每个节点是它的子树里面节点的后缀。
    通常一个串包含于另一个串我们理解为:一个串是另一个串的前缀的后缀。(因为前缀是节点,后缀在fail树上有意义)
    当然这道题直接求FAIL树中该节点子树的cnt和。
  • easy code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
char s[N];
int pos[N],n;
struct AC {
	int fail[N],nd,go[N][27],cnt[N],Q[N],hd,tl,st[N],tp;
	bool mark[N];
	AC() {nd=tl=0;hd=1;}
	void Insert(int &u) {
		u=0;
		int sz=strlen(s);
		for(int i=0;i<sz;i++) {
			int x=s[i]-'a';
			if(!go[u][x])go[u][x]=++nd;
			u=go[u][x];
			cnt[u]++;
		}
	}
	void gt_fail() {
		for(int i=0;i<26;i++) if(go[0][i])Q[++tl]=go[0][i];
		while(hd<=tl) {
			int u=Q[hd++];st[++tp]=u;
			for(int i=0;i<26;i++) {
				if(go[u][i]) fail[go[u][i]]=go[fail[u]][i],Q[++tl]=go[u][i];
				else go[u][i]=go[fail[u]][i];
			}
		}
	}
	void solve() {
		while(tp) {int u=st[tp--];cnt[fail[u]]+=cnt[u];}
		for(int i=1;i<=n;i++) printf("%d\n",cnt[pos[i]]);
	}
}A;
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {scanf("%s",s);A.Insert(pos[i]);}
	A.gt_fail();
	A.solve();
	return 0;
}