Codeforces Round 70 E. You Are Given Some Strings...(AC自动机)
题意:给出一个文本串T,再给出N个模式串,求任意两个模式串拼接后在T中出现的次数的和(可重叠)
思路:首先暴力是不可能暴力的。可以考虑枚举两个串的结合点在T上出现的位置i,那么这个位置对答案的贡献就是以i为结尾的串的数量乘上以i+1为起始位置的串的数量。求以每个位置为起点和终点的串的数量可以用AC自动机。正向跑一遍AC自动机可以求出结尾的数量,反向跑一遍可以求出起点的数量,然后枚举结合点统计答案即可,具体看代码。
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
struct AC_automaton
{
int tree[maxn][30];
int fail[maxn];
int num[maxn];
int tot;
int pos[maxn];
void Insert(char *s)
{
int root=0;
for(int i = 0;s[i];++i)
{
int tmp=s[i]-'a';
if(!tree[root][tmp])tree[root][tmp]=++tot;
root=tree[root][tmp];
}
num[root]++;
}
void get_fail()
{
queue<int>q;
for(int i = 0;i < 26;++i)
if(tree[0][i])q.push(tree[0][i]);
while(!q.empty())
{
int now=q.front();
q.pop();
num[now]+=num[fail[now]];//提前加上,匹配的时候就不用跳fail了
for(int i = 0;i < 26;++i)
{
if(tree[now][i])
fail[tree[now][i]]=tree[fail[now]][i],q.push(tree[now][i]);
else
tree[now][i]=tree[fail[now]][i];
}
}
}
void match(char *s)
{
int root=0;
for(int i = 0;s[i];++i)
{
root=tree[root][s[i]-'a'];
pos[i]=num[root];
}
}
}AC2,AC1;
int main()
{
char s[maxn],a[maxn];
scanf("%s",s);
int n;
scanf("%d",&n);
for(int i = 1;i <= n;++i)
{
scanf("%s",a);
AC2.Insert(a);
reverse(a,a+strlen(a));//建立反向模式串的自动机
AC1.Insert(a);
}
AC1.get_fail();
AC2.get_fail();
AC2.match(s);
int ns=strlen(s);
reverse(s,s+ns);
AC1.match(s);
long long ans=0;
for(int i = 0;i < ns-1;++i)
ans+=1ll*AC2.pos[i]*AC1.pos[ns-i-2];
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号