题目描述:
qwq.
题目分析:
对fail指针的理解还是不够哇qwq
fail[u]所代表的串,其实是u所代表的串的后缀.
而后缀可以表示出所有的子串
每个节点初始值为1,代表前缀出现过一次
从 u->fail[u]连一条边,那么fail[u]这个串的出现的次数,就是它的fail树子树和
用倒叙BFS序进行统计
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
const int maxm=2e6+100;
int ans[maxm],pos[maxm],num[maxm],l[maxm],n,cnt;
std::string s1;
int idx(char c)
{
if(c=='*') return 26;
return c-'a';
}
namespace AC_automaton{
std::queue <int> dl;
struct node{
int fail;
int ch[27];
}st[maxm];
int sz;
inline int insert(std::string s)
{
int now=0;
int len=s.length();
for(int i=0;i<len;i++)
{
if(!st[now].ch[s[i]-'a']) st[now].ch[s[i]-'a']=++sz;
now=st[now].ch[s[i]-'a'];
ans[now]++;
}
return now;
}
inline void makefail()
{
for(int i=0;i<26;i++)
if(st[0].ch[i]) dl.push(st[0].ch[i]);
while(!dl.empty())
{
int now=dl.front();
dl.pop();
l[++cnt]=now;
for(int i=0;i<26;i++)
{
if(st[now].ch[i])
{
st[st[now].ch[i]].fail=st[st[now].fail].ch[i];
dl.push(st[now].ch[i]);
}
else
st[now].ch[i]=st[st[now].fail].ch[i];
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
std::cin>>s1;
pos[i]=AC_automaton::insert(s1);
}
AC_automaton::makefail();
for(int i=cnt;i>=1;i--)
ans[AC_automaton::st[l[i]].fail]+=ans[l[i]];
for(int i=1;i<=n;i++)
printf("%d\n",ans[pos[i]]);
return 0;
}