1. 程式人生 > >【AC自動機】bzoj4327: JSOI2012 玄武密碼

【AC自動機】bzoj4327: JSOI2012 玄武密碼

++ ron 密碼 開始 match 節點 ac自動機 描述 node

題目思路沒話講;主要是做題時候的細節和經驗

Description

在美麗的玄武湖畔,雞鳴寺邊,雞籠山前,有一塊富饒而秀美的土地,人們喚作進香河。相傳一日,一縷紫氣從天而至,只一瞬間便消失在了進香河中。老人們說,這是玄武神靈將天書藏匿在此。 很多年後,人們終於在進香河地區發現了帶有玄武密碼的文字。更加神奇的是,這份帶有玄武密碼的文字,與玄武湖南岸臺城的結構有微妙的關聯。於是,漫長的破譯工作開始了。 經過分析,我們可以用東南西北四個方向來描述臺城城磚的擺放,不妨用一個長度為N的序列來描述,序列中的元素分別是‘E’,‘S’,‘W’,‘N’,代表了東南西北四向,我們稱之為母串。而神秘的玄武密碼是由四象的圖案描述而成的M段文字。這裏的四象,分別是東之青龍,西之白虎,南之朱雀,北之玄武,對東南西北四向相對應。 現在,考古工作者遇到了一個難題。對於每一段文字,其前綴在母串上的最大匹配長度是多少呢?

Input

第一行有兩個整數,N和M,分別表示母串的長度和文字段的個數。 第二行是一個長度為N的字符串,所有字符都滿足是E,S,W和N中的一個。 之後M行,每行有一個字符串,描述了一段帶有玄武密碼的文字。依然滿足,所有字符都滿足是E,S,W和N中的一個。

Output

輸出有M行,對應M段文字。 每一行輸出一個數,表示這一段文字的前綴與母串的最大匹配串長度。

Sample Input

7 3
SNNSSNS
NNSS
NNN
WSEE

Sample Output

4
2
0

HINT

對於100%的數據,N<=10^7,M<=10^5,每一段文字的長度<=100。


題目分析

題目的做法很顯然:標記一下哪些節點被匹配過,之後再dfs找被匹配過的最大深度。

這裏記錄一下做題經驗:

  1. 字符集能壓縮的盡量壓縮(和離散化一個道理),trie畢竟還有|S|的常數在這裏。
  2. 查詢跳匹配的時候for (int v=u; v&&!vis[v]; v=nxt[v])!vis[v]這個優化非常重要

以上。

 1 #include<bits/stdc++.h>
 2 const int maxNode = 10000035;
 3 const int maxn = 10000035;
 4 
 5 char s[maxn],t[103
]; 6 int n,m,c[100035],l[100035],cnt; 7 struct ACAutomaton 8 { 9 bool vis[maxNode]; 10 std::queue<int> q; 11 int nxt[maxNode],f[maxNode][5],fa[maxNode],tot; 12 13 inline int get(char ch) 14 { 15 if(ch==W) return 0; 16 if(ch==E) return 1; 17 if(ch==S) return 2; 18 return 3; 19 } 20 void insert(char *s) 21 { 22 int u = 1, lens = strlen(s); 23 for (int i=0; i<lens; i++) 24 { 25 int c = get(s[i]); 26 if (!f[u][c]) f[u][c] = ++tot, fa[f[u][c]] = u; 27 u = f[u][c]; 28 } 29 c[++cnt] = u; 30 } 31 void build() 32 { 33 for (int i=0; i<4; i++) f[0][i] = 1; 34 q.push(1); 35 while (q.size()) 36 { 37 int tt = q.front(); 38 q.pop(); 39 for (int i=0; i<4; i++) 40 if (f[tt][i]) 41 nxt[f[tt][i]] = f[nxt[tt]][i], q.push(f[tt][i]); 42 else f[tt][i] = f[nxt[tt]][i]; 43 } 44 } 45 int query(int s, int t) 46 { 47 for (; s; s=fa[s], t--) 48 if (vis[s]) return t; 49 return 0; 50 } 51 void match(char *s) 52 { 53 int u = 1, lens = strlen(s); 54 for (int i=0; i<lens; i++) 55 { 56 int c = get(s[i]); 57 u = f[u][c]; 58 for (int v=u; v&&!vis[v]; v=nxt[v]) vis[v] = 1; 59 } 60 } 61 }f; 62 63 int main() 64 { 65 scanf("%d%d%s",&n,&m,s); 66 f.tot = 1; 67 for (int i=1; i<=m; i++) 68 { 69 scanf("%s",t); 70 f.insert(t); 71 l[i] = strlen(t); 72 } 73 f.build(), f.match(s); 74 for (int i=1; i<=m; i++) 75 printf("%d\n",f.query(c[i], l[i])); 76 return 0; 77 }

END

【AC自動機】bzoj4327: JSOI2012 玄武密碼