1. 程式人生 > >BZOJ 4327: JSOI2012 玄武密碼

BZOJ 4327: JSOI2012 玄武密碼

arch sca 美麗 font 字符 結構 spa class 美的

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。

Solution:

  本題AC自動機+簡單搜索就好了。

  存下主串,再對所有模式串構建AC自動機(記錄下每個串結尾的節點和每個節點的前趨以及深度),然後直接在trie樹中查詢主串,對於掃到的節點p,標記所有沒訪問過的fail[p]節點(訪問過的就不用再往上標記了),然後直接從每個模式串的尾節點往上dfs,搜到的第一個被標記的節點的深度就是答案了。

代碼:

 1 #include<bits/stdc++.h>
 2 #define il inline
 3
#define ll long long 4 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) 5 #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) 6 using namespace std; 7 const int N=1000005; 8 int n,m,trie[N][4],cnt,ed[N],fail[N],dep[N],pre[N]; 9 char t[N*10],s[N]; 10 bool vis[N]; 11 12 il int id(char c){ 13 if(c==E)return 0; 14 if(c==S)return 1; 15 if(c==W)return 2; 16 return 3; 17 } 18 19 il void insert(char *s,int num){ 20 int len=strlen(s),p=0,x; 21 For(i,0,len-1){ 22 x=id(s[i]); 23 if(!trie[p][x])dep[++cnt]=dep[p]+1,trie[p][x]=cnt,pre[cnt]=p; 24 p=trie[p][x]; 25 } 26 ed[num]=p; 27 } 28 29 il void bfs(){ 30 queue<int>q; 31 For(i,0,3)if(trie[0][i])q.push(trie[0][i]); 32 while(!q.empty()){ 33 int u=q.front();q.pop(); 34 For(i,0,3) { 35 int v=trie[u][i]; 36 if(v) fail[v]=trie[fail[u]][i],q.push(v); 37 else trie[u][i]=trie[fail[u]][i]; 38 } 39 } 40 } 41 42 il int dfs(int now){ 43 if(vis[now])return dep[now]; 44 if(!now) return 0; 45 dfs(pre[now]); 46 } 47 48 il void search(char *s){ 49 int len=strlen(s),p=0; 50 For(i,0,len-1){ 51 p=trie[p][id(s[i])]; 52 for(int j=p;j&&!vis[j];j=fail[j]) vis[j]=1; 53 } 54 For(i,1,m) printf("%d\n",dfs(ed[i])); 55 } 56 57 int main(){ 58 scanf("%d%d%s",&n,&m,t); 59 For(i,1,m) scanf("%s",s),insert(s,i); 60 bfs(); 61 search(t); 62 return 0; 63 }

BZOJ 4327: JSOI2012 玄武密碼