CF1202E You Are Given Some Strings...

给出一个字符串t和n个字符串

设f(t,s)为s在t中的出现次数。

\(sum_{i=1}^n\sum_{j=1}^nf(t,s_i+s_j)\)

枚举划分点。

对t的每个划分点x,处理出有多少个字符串是当前t[0,x]的后缀。

然后反着建AC自动机,反着枚举划分点x,处理出有多少个字符串是当前t[x,n]的后缀。

处理有多少个字符串是当前位置的前缀:建出fail树,求出根节点到当前位置的路径?

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
int n,tr[maxn][26],fail[maxn],tot;
vector<int> g[maxn];
int cnt[maxn];
string t,s[maxn];
int pre[maxn],lst[maxn];
void insert (string s) {
	int u=0;
	for (char i:s) {
		if (!tr[u][i-'a']) tr[u][i-'a']=++tot;
		u=tr[u][i-'a'];
	}
	cnt[u]++;
}
void build () {
	queue<int> q;
	for (int i=0;i<26;i++) {
		if (tr[0][i]) {
			q.push(tr[0][i]);
		}
	}
	while (q.size()) {
		int u=q.front();
		q.pop();
		for (int i=0;i<26;i++) {
			if (tr[u][i]) {
				fail[tr[u][i]]=tr[fail[u]][i];
				q.push(tr[u][i]);
			}
			else {
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
}
void dfs (int u,int f) {
	cnt[u]+=cnt[f];
	for (int v:g[u]) {
		if (v==f) continue;
		dfs(v,u);
	}
}
int main () {
	ios::sync_with_stdio(false);
	cin>>t;
	cin>>n;
	for (int i=1;i<=n;i++) {
		cin>>s[i];
		insert(s[i]);
	}
	build();
	for (int i=1;i<=tot;i++) {
		g[i].push_back(fail[i]);
		g[fail[i]].push_back(i);
	}
	dfs(0,0);
	int u=0;
	for (int i=0;i<t.size();i++) {
		u=tr[u][t[i]-'a'];
		pre[i]=cnt[u];
	}
	
	for (int i=0;i<=tot;i++) for (int j=0;j<26;j++) tr[i][j]=0;
	for (int i=0;i<=tot;i++) cnt[i]=fail[i]=0,g[i].clear();
	tot=0;
	for (int i=1;i<=n;i++) reverse(s[i].begin(),s[i].end());
	for (int i=1;i<=n;i++) {
		insert(s[i]);
	}
	build();
	for (int i=1;i<=tot;i++) {
		g[i].push_back(fail[i]);
		g[fail[i]].push_back(i);
	}
	dfs(0,0);
	u=0;
	for (int i=t.size()-1;i>=0;i--) {
		u=tr[u][t[i]-'a'];
		lst[i]=cnt[u];
	}
	long long ans=0;
	for (int i=0;i<t.size()-1;i++) {
		ans+=1ll*pre[i]*lst[i+1];
	}
	printf("%lld\n",ans);
} 
posted @ 2021-07-13 12:52  zlc0405  阅读(24)  评论(0编辑  收藏  举报