4327: JSOI2012 玄武密码

4327: JSOI2012 玄武密码

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自动机题目。首先发现多模式串,二话不说上AC自动机,但是发现只有‘E’、‘S’、‘W’、‘N’这四个字母,所以可以优化一下建立起从‘E’、‘S’、‘W’、‘N’到‘a’、‘b’、‘c’、‘d’的映射就好了,然后要查询每个串的前缀与母串的最大匹配长度,那我们就在trie树上标记母串所有的可以匹配到的位置,然后对于每一个模式串,我们就从它的trie树上进行检索,当发现第一个没有被母串匹配到的结点时,当前长度就是最大的匹配长度。另外还有一个小小的优化,如果说对于一个节点它已经被标记过了,那么它的next链上的所有点一定也被标记了,所以这时就可以直接break。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<string>
  5 #include<cstring>
  6 #include<map>
  7 #include<queue>
  8 #include<stack>
  9 #include<algorithm>
 10 #include<vector>
 11 #define man 100005
 12 #define maxn 10000005
 13 using namespace std;
 14 
 15 inline int read()
 16 {
 17     char c=getchar();
 18     int res=0,x=1;
 19     while(c<'0'||c>'9')
 20     {
 21         if(c=='-')
 22         x=-1;
 23         c=getchar();
 24     }
 25     while(c>='0'&&c<='9')
 26     {
 27         res=res*10+(c-'0');
 28         c=getchar();
 29     }
 30     return res*x;
 31 }
 32 
 33 int n,m,tot=1;
 34 int tree[maxn][5],nt[maxn],bo[maxn],f[maxn];
 35 char a[maxn],d[4],b[man][105];
 36 queue<int>q;
 37 
 38 char pd(char c)
 39 {
 40     if(c=='E') return d[0];
 41     else if(c=='S') return d[1];
 42     else if(c=='W') return d[2];
 43     else return d[3];
 44 }
 45 
 46 void trie(char *s)
 47 {
 48     int len=strlen(s),u=1;
 49     for(register int i=0;i<len;i++)
 50     {
 51         int c=s[i]-'a';
 52         if(!tree[u][c])
 53         tree[u][c]=++tot;
 54         u=tree[u][c];
 55     }
 56     bo[u]=1;
 57 }
 58 
 59 void bfs()
 60 {
 61     for(register int i=0;i<=3;i++)
 62     tree[0][i]=1;
 63     nt[1]=0;q.push(1);
 64     while(q.size())
 65     {
 66         int u=q.front();q.pop();
 67         for(register int i=0;i<=3;i++)
 68         {
 69             if(!tree[u][i])
 70             tree[u][i]=tree[nt[u]][i];
 71             else
 72             {
 73                 int v=tree[u][i];
 74                 q.push(v);
 75                 nt[v]=tree[nt[u]][i];
 76             }
 77         }
 78     }
 79 }
 80 
 81 void find(char *s)
 82 {
 83     int len=strlen(s),u=1,k;
 84     for(register int i=0;i<len;i++)
 85     {
 86         int c=s[i]-'a';
 87         k=tree[u][c];
 88         while(k>1)
 89         {
 90             if(f[k]) break;//小小的优化 原理就是这个节点被标记过时,它的 
 91             f[k]=1;//next链上的点也一点被标记了,所以没必要再跳一次next 
 92             k=nt[k];//链,直接break掉就好了。 
 93         }
 94         u=tree[u][c];
 95     }
 96 }
 97 
 98 int ask(char *s)
 99 {
100     int len=strlen(s),u=1;
101     for(register int i=0;i<len;i++)
102     {
103         int c=s[i]-'a';
104         u=tree[u][c];
105         if(!f[u]) return i;
106     }
107     return len;
108 }
109 
110 int main()
111 {
112     n=read();m=read();
113     d[0]='a';d[1]='b';d[2]='c';d[3]='d';
114     scanf("%s",a);
115     for(register int i=0;i<n;i++)
116     {
117         a[i]=pd(a[i]);
118     }
119     for(register int i=1;i<=m;i++)
120     {
121         scanf("%s",b[i]);
122         int len=strlen(b[i]);
123         for(register int j=0;j<len;j++)
124         {
125             b[i][j]=pd(b[i][j]);
126         }
127         trie(b[i]);
128     }
129     bfs();
130     find(a);
131     for(register int i=1;i<=m;i++)
132     {
133         printf("%d\n",ask(b[i]));
134     }
135     return 0;
136 }
View Code

 

posted @ 2019-04-10 17:07  snowy2002  阅读(316)  评论(0编辑  收藏  举报