【BZOJ3172】單詞(AC自動機)
阿新 • • 發佈:2018-01-09
sca ++ include 很多 queue 有一點 div body ont
然後你就可以yy所有單詞中間有一個空格之類的東西
【BZOJ3172】單詞(AC自動機)
題面
Description
某人讀論文,一篇論文是由許多單詞組成。但他發現一個單詞會在論文中出現很多次,現在想知道每個單詞分別在論文中出現多少次。
Input
第一個一個整數N,表示有多少個單詞,接下來N行每行一個單詞。每個單詞由小寫字母組成,N<=200,單詞長度不超過10^6
Output
輸出N個整數,第i行的數字表示第i個單詞在文章中出現了多少次。
Sample Input
3
a
aa
aaa
Sample Output
6
3
1
題解
yyb因為調這道題而死亡
在重復一下題目的意思把。。。
看不見文章對不讀,,
那是因為文章就是所有單詞組成的
很明顯的AC自動機,
然後,我們
每次把每個單詞帶進去匹配一下
暴跳fail指針
美滋滋的收獲90分
0分是空間玩炸了。。。
暴跳fail指針是可以被卡炸的。。。
所以,我們就不暴跳了呀
每次要跳的時候就在這個點這裏打一個標記
所有標記打完之後
我們就從底下往上一層層跳(記一下bfs序倒著跳)
每次就只跳一層,然後標記丟到上面去
這樣就可以一起跳啦
然後美滋滋的 AC啦
對了,
這題還有一點
就是會有重復的單詞。。。
所以要記錄一下每個單詞和哪個單詞是一樣的(不是並查集)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 1100000
struct Node
{
int vis[26];
int fail,ff;
int id,sum;
}t[MAX];
int tot,lid[500 ];
int ans[500];
int n,q[MAX],tp;
char ch[MAX],ss[MAX];
void Insert(int id,char *s)
{
int gg=strlen(s);
int now=0;
for(int i=0;i<gg;++i)
{
if(!t[now].vis[s[i]-'a'])
t[now].vis[s[i]-'a']=++tot;
t[t[now].vis[s[i]-'a']].ff=now;
now=t[now].vis[s[i]-'a'];
}
if(!t[now].id)t[now].id=id,lid[id]=id;
else lid[id]=t[now].id;
}
queue<int> Q;
void GetFail()
{
for(int i=0;i<26;++i)
if(t[0].vis[i])
Q.push(q[++tp]=t[0].vis[i]);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=0;i<26;++i)
{
if(t[u].vis[i])
t[t[u].vis[i]].fail=t[t[u].fail].vis[i],Q.push(q[++tp]=t[u].vis[i]);
else
t[u].vis[i]=t[t[u].fail].vis[i];
}
}
}
int l=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%s",ch);
Insert(i,ch);
int len=strlen(ch);
for(int j=0;j<len;++j)
ss[l++]=ch[j];
ss[l++]='#';
}
GetFail();
int now=0;
for(int j=0;j<l;++j)
{
if(ss[j]=='#')now=0;
else now=t[now].vis[ss[j]-'a'];
t[now].sum++;
}
for(int i=tp;i;i--)
{
ans[t[q[i]].id]+=t[q[i]].sum;
t[t[q[i]].fail].sum+=t[q[i]].sum;
}
for(int i=1;i<=n;++i)printf("%d\n",ans[lid[i]]);
return 0;
}
【BZOJ3172】單詞(AC自動機)