bzoj 3172(AC自动机)

3172: [Tjoi2013]单词

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 2109   Solved: 978
[ Submit][ Status][ Discuss]

Description

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

Input

第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6

Output

输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。

Sample Input

3
a
aa
aaa

Sample Output

6
3

1


解题思路:

首先先跑一遍AC自动机,然后记录下每个点的失配边所连其它点,然后确定主串后,进行一次匹配,并且把每次在的点的sum+1;最后对n个单词进行一次BFS统计其它连与它,或间接连与它的点的sum就可以了。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int uk;
char c[1100001];
char s[2100001];
const int M_ax=1100001;
int lw=0;
struct data
 {
  int to,next;
 }e[2100001];
int h[1100001];
int v[211];
bool ch[1100001][26];
int dui[1100001][26];
int f[1100001];
int q[1100001];
int summ[1100001];
int now_sum;


void insert(int x,int y)
 {
  ++lw;
  e[lw].to=y;
  e[lw].next=h[x];
  h[x]=lw;
 }


int change(char c)
 {
  int sug=int(c)-96;
  return sug;
 }


void dfs(int now,int len,int xian,int o)
 {
  if (len>xian) {v[o]=now;return;}
  int zan=change(c[len-1]);
    if (ch[now][zan])
     {
      dfs(dui[now][zan],len+1,xian,o);
}else
 {
  ch[now][zan]=true; ++now_sum;
  dui[now][zan]=now_sum;
  dfs(now_sum,len+1,xian,o);
 }
 }


void Init(int o)
 {
  getchar();
  scanf("%s",c); int len=strlen(c); 
  for (int i=0;i<=len-1;++i)
  {
  ++uk;
  s[uk]=c[i];
 }
++uk; s[uk]='-';
  dfs(0,1,len,o);
 }


void ready()
 {
  f[0]=-1; int tail=1; int head=0; q[tail]=0;
  while (head!=tail)
  {
  head=(head+1)%M_ax;
  for (int i=1;i<=26;++i)
  if (ch[q[head]][i])
   {
     int now=f[q[head]];
     while (now!=-1 && !ch[now][i])
      {
        now=f[now];
    }
   if (now==-1) now=0;else now=dui[now][i];
   f[dui[q[head]][i]]=now; insert(now,dui[q[head]][i]);
tail=(tail+1)%M_ax;
q[tail]=dui[q[head]][i];
 }
 }
 }


int main()
{
uk=0;
scanf("%d",&n); now_sum=0;
for (int i=1;i<=n;++i)
{
Init(i);
}
--uk;
ready();
    int now=0;
for (int i=1;i<=uk;++i)
{
int zan=change(s[i]); 
if (s[i]=='-')
{
now=0; continue;
 }
while (now!=-1 && !ch[now][zan]) now=f[now];
   if (now!=-1)
    {
    ++summ[dui[now][zan]];
    now=dui[now][zan];
}else now=0;
}
for (int i=1;i<=n;++i)
{
int now=v[i];
long long sug=0;
int tail=1; int head=0; q[tail]=v[i];
while (head!=tail)
{
head=(head+1)%M_ax;
sug+=summ[q[head]];
int u=h[q[head]];
while (u!=0)
{
tail=(tail+1)%M_ax;
q[tail]=e[u].to;
   u=e[u].next;
 }
 }
cout<<sug<<endl;
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值