1. 程式人生 > >BZOJ4327 [JSOI2012] 玄武密碼 [AC自動機]

BZOJ4327 [JSOI2012] 玄武密碼 [AC自動機]

lee turn its bre 序列 16px 數據 using nod

  題目傳送門

玄武密碼

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。


  分析:

  AC自動機的好題。

  建Trie樹的時候記錄一下每個節點的父節點和每一段模式串的終點,然後建立$fail$指針再跑一邊文本串,再經過了的節點打上標記,然後對每一個模式串詢問,從終點一直往根節點跳,只要遇到打上標記的點就直接返回答案(也就是該模式串總長減去遍歷經過的節點數)。

  Code:

//It is made by HolseLee on 13rd Aug 2018
//BZOJ4327
#include<bits/stdc++.h> using namespace std; const int N=1e7+7; const int M=101; const int maxn=5e5+7; int n,m,tot,fail[maxn],fa[maxn],point[maxn],lo[maxn/5]; char s[N],a[M]; struct Node{ int nxt[4];bool vis; }t[maxn]; queue<int>team; struct Trie{ int get(char ch) { if(ch==E)return 0; if(ch==S)return 1; if(ch==W)return 2; if(ch==N)return 3; } void ins(char *ch,int x) { int root=0,k,len=strlen(ch); for(int i=0;i<len;++i){ k=get(ch[i]); if(!t[root].nxt[k]) t[root].nxt[k]=++tot, fa[t[root].nxt[k]]=root; root=t[root].nxt[k]; } point[x]=root; } void getfail() { int root=0; for(int i=0;i<4;++i){ if(t[root].nxt[i]) fail[t[root].nxt[i]]=0, team.push(t[root].nxt[i]); } while(!team.empty()){ root=team.front();team.pop(); for(int i=0;i<4;++i) if(t[root].nxt[i]){ fail[t[root].nxt[i]]=t[fail[root]].nxt[i]; team.push(t[root].nxt[i]); } else t[root].nxt[i]=t[fail[root]].nxt[i]; } } void pre() { for(int i=0;i<=tot;++i)t[i].vis=false; int root=0,id; for(int i=0;i<n;++i){ id=get(s[i]); root=t[root].nxt[id]; for(int j=root;j;j=fail[j]) if(t[j].vis)break; else t[j].vis=1; } } int quary(int x) { int ret=lo[x]; for(int i=point[x];i;i=fa[i]){ if(t[i].vis)return ret; ret--; } return 0; } }T; int main() { scanf("%d%d%s",&n,&m,s); for(int i=1;i<=m;++i){ scanf("%s",a); lo[i]=strlen(a); T.ins(a,i); } T.getfail();T.pre(); for(int i=1;i<=m;++i) printf("%d\n",T.quary(i)); return 0; }

BZOJ4327 [JSOI2012] 玄武密碼 [AC自動機]