1. 程式人生 > >CODEVS 2542單詞__fail樹

CODEVS 2542單詞__fail樹

def 如何 app close 小寫字母 我們 輸出 但是 small

2542 單詞

2013年省隊選拔賽天津市隊選拔賽

時間限制: 2 s 空間限制: 256000 KB 題目等級 : 大師 Master 題目描述 Description

小張最近在忙畢業,所以一直在讀論文。一篇論文是由許多單詞組成的。

但小張發現一個單詞會在論文中出現很多次,他想知道每個單詞分別在論文中出現了多少次。

輸入描述 Input Description

第一行一個整數N,表示有N個單詞,接下來N行,每行一個單詞,每個單詞都由小寫字母組成(N<=200)

輸出描述 Output Description

輸出N個整數,第i行的數表示第i個單詞在文章中出現了多少次。

樣例輸入 Sample Input

3
a
aa
aaa

樣例輸出 Sample Output

6

3

1

數據範圍及提示 Data Size & Hint

30%的數據 單詞總長度不超過10^3
100%的數據 單詞總長度不超過10^6

————————————————————————————————————————————————————————————————————————————————

一看是AC自動機是沒有問題的,但是建完AC自動機後該如何做呢?

當然可以直接用AC自動機的fail指針向前跳,但是這樣要枚舉單詞內的每個位置,嚴重超時。

這裏要用到一個很有用的工具——fail樹。

當AC自動機建完以後,我們發現每一個節點都只有一個失敗指針(根節點的可以忽略),且沿著失敗指針最終能走到根節點。

這樣我們將失敗指針反向就可以變成一棵樹。

這棵樹的有些重要的性質:

1、以x節點為根的子樹中每個節點代表的字符串都以x點所代表的字符串為後綴。

2、節點x代表的字符串的最大後綴為父節點代表的字符串。

這樣我們在插入字符串是把經過的每個節點的val值都加1,就相當於統計的對應前綴出現的次數。

然後通過dfs將val變為val加它所有子樹的val和。也就是以(s)為前綴的字符串個數+以(*+s)為前綴的字符串的個數+以(**+s)為前綴的個數+。。。。。

這樣s出現的次數就是s串尾對應的val值。

比較難想,但是fail樹真的很有用。

————————————————————————————————————————————————————————————————————————————————

技術分享
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+3;
 4 const int maxs=26;
 5 struct edge{
 6     int u,v,next;
 7 }e[maxn];
 8 int head[maxn],js=0;
 9 void addage(int u,int v)
10 {
11     e[++js].u=u;e[js].v=v;
12     e[js].next=head[u];head[u]=js;
13 }
14 struct AC{
15     int ch[maxn][maxs];
16     int val[maxn],f[maxn];
17     int sz;
18     AC(){
19         memset(ch[0],0,sizeof(ch[0]));
20         val[0]=0;
21         sz=1;
22     }
23     int idx(char c){
24         return c-a;
25     }
26     int insert(char *s){
27         int n=strlen(s),cur=0;
28         for(int i=0,c;i<n;i++){
29             c=idx(s[i]);
30             if(!ch[cur][c]){
31                 memset(ch[sz],0,sizeof(sz));
32                 val[sz]=0;
33                 ch[cur][c]=sz++;
34             }
35             cur=ch[cur][c];
36             val[cur]++;
37         }
38         return cur;
39     }
40     void getfail(){
41         queue<int>q;
42         f[0]=0;
43         for(int c=0;c<maxs;c++){
44             int v=ch[0][c];
45             if(v){
46                 f[v]=0;
47                 q.push(v);
48                 addage(0,v);
49             }            
50         }
51         while(!q.empty()){
52             int u=q.front();q.pop();
53             for(int c=0;c<maxs;++c){
54                 int v=ch[u][c];
55                 if(v){
56                     q.push(v);
57                     int r=f[u];
58                     while(r && !ch[r][c])r=f[r];
59                     f[v]=ch[r][c];
60                     addage(f[v],v);
61                 }
62             }
63         }
64     }
65 }ac;
66 int n;
67 int wz[201];
68 char s[maxn];
69 void dfs(int u,int f)
70 {
71     for(int i=head[u];i;i=e[i].next){
72         int u=e[i].u,v=e[i].v;
73         if(v==f)continue;
74         dfs(v,u);
75         ac.val[u]+=ac.val[v];
76     }
77 }
78 int main()
79 {
80     scanf("%d",&n);
81     for(int i=0;i<n;++i){
82         scanf("%s",s);
83         wz[i]=ac.insert(s);
84     }
85     ac.getfail();
86     dfs(0,-1);
87     for(int i=0;i<n;i++)
88         printf("%d\n",ac.val[wz[i]]);
89     return 0;
90 }
View Code

CODEVS 2542單詞__fail樹