题意简述
给定 个单词,对每个单词求出 个单词中总共包含多少这个单词(自己也算,也就是说答案至少为 )。比如三个单词分别是 ,那么 出现 次(自己一次, 中两次)。
思路
每个字符接在一起,中间插入一个特殊字符,然后跑一遍 自动机即珂。
具体的思维过程
自动机的模板问题:给定一个文本串,和一些模式串,求每个模式串在文本串中出现了多少次。
实际上我们珂以把这个问题转化成模板问题。由于我们的匹配不能跨单词,只能限于每个单词之间。最好的解决方法就是构造这样的情况:跨单词肯定得失配。
这好办,只要把所有单词接在一起,然后在中间加一个特殊字符,使得它不等于 中的任意一个,那跨单词的时候肯定就失配了(因为模式串中只有 )比如说设成 中的字符 ,经验证,这个字符是左大括号 。然后写 自动机的时候,就当字母有 个字母去建树即珂。这题就被完美的套成了模板解决了。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 212345
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u);~i;i=G.Next(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
int id[N];
class AC_Automaton
{
public:
int tr[N][27];
int tot;
void Init()
{
FK(tr);
tot=1;
}
int Insert(char s[])
{
int pos=1;
F(i,0,INT_MAX)
{
if (!s[i]) break;
int c=s[i]-'a';
if (!tr[pos][c])
{
tr[pos][c]=++tot;
}
pos=tr[pos][c];
}
return pos;
}
int fail[N];
queue<int> Q;
vector<int> G[N];void Add(int u,int v){G[u].push_back(v);}
void BuildFail()
{
while(!Q.empty()) Q.pop();
F(i,0,26) tr[0][i]=1;
Q.push(1);
while(!Q.empty())
{
int u=Q.front();Q.pop();
F(i,0,26)
{
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];
}
}
F(i,2,tot) Add(fail[i],i);
}
int size[N];
void DFS(int u)
{
F(i,0,(int)G[u].size()-1)
{
int v=G[u][i];
DFS(v);
size[u]+=size[v];
}
}
void Query(char s[])
{
int pos=1;
F(i,0,INT_MAX)
{
if (!s[i]) break;
int c=s[i]-'a';
pos=tr[pos][c];
++size[pos];
}
DFS(1);
}
}AC;
int n;
char s[N];
char T[N*10];
void Input()
{
scanf("%d",&n);
AC.Init();
FK(T);
F(i,1,n)
{
scanf("%s",s);
id[i]=AC.Insert(s);
strcat(T,s);
strcat(T,"{");//'{'='z'+1
}
}
void Soviet()
{
// printf("T=%s\n",T);
AC.BuildFail();
AC.Query(T);
F(i,1,n)
{
printf("%d\n",AC.size[id[i]]);
}
}
void IsMyWife()
{
Input();
Soviet();
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}