zoukankan      html  css  js  c++  java
  • 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
    ",ask(b[i]));
    134     }
    135     return 0;
    136 }
    View Code
  • 相关阅读:
    Linux 下IOport编程訪问
    Xcode下执行HelloWorld
    PHP/HTML混写的四种方式总结
    php取两位小数的几种方法
    使用原生JS+CSS或HTML5实现简单的进度条和滑动条效果(精问)
    js进阶 9-7 自动计算商品价值
    html5--1.12表格详解
    html5常用标签table表格布局
    html常用属性border-radius、linear-gradient怎么使用
    类选择器选择非唯一属性无法精确取值的问题
  • 原文地址:https://www.cnblogs.com/snowy2002/p/10684633.html
Copyright © 2011-2022 走看看