zoukankan      html  css  js  c++  java
  • UVA 10679 I Love Strings

    传送门

    题目大意

    给定文本串$S$和若干模式串${T}$, 对每个模式串$T$, 询问$T$是否为$S$的子串.

    Solution

    裸的AC自动机, 也可以用后缀数组做.

    P.S. 这题数据很弱, 朴素的字符串匹配也能过.

    Pitfalls

    模式串有重复的. 这样, 在建TRIE时就不能直接对每个模式串对应的节点 (尾节点) 标记上模式串的序号, 否则对于重复出现的模式串, 最后一次出现的那个会把在它之前的那些覆盖掉.

    正确的做法是, 对于每个尾节点作唯一标号. 另外维护一个表$idx[]$, $idx[i]$表示第$i$个模式串的尾节点的标号.

    另外要注意AC自动机的某些易错的实现细节, 代码注释有提及.

    Implementation

    注释比较详细, 可作为模板.

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int N{1<<20}, M{1<<10};
     5 
     6 bool res[M];
     7 int idx[M];
     8 char s[N], t[M];
     9 int ch[N][52], id[N], fail[N], last[N];
    10 
    11 
    12 int get_id(char ch){
    13     return islower(ch)?ch-'a':ch-'A'+26;
    14 }
    15 
    16 queue<int> que;
    17 
    18 int main(){
    19     // cout<<int('a')<<' '<<int('z')<<' '<<int('A')<<' '<<int('Z')<<endl;
    20     int T;
    21     for(cin>>T; T--; ){
    22         int q;
    23         scanf("%s%d", s, &q);
    24         int tail=0;
    25 
    26         memset(res, false, sizeof(res));    //error-prone
    27 
    28         memset(ch[tail], 0, sizeof(ch[tail]));
    29         tail++;
    30 
    31         for(int i=1; i<=q; i++){
    32             scanf("%s", t);
    33             int u=0;
    34             for(int j=0; t[j]; j++){
    35                 int &v=ch[u][get_id(t[j])];
    36                 if(!v){
    37                     v=tail++;
    38                     memset(ch[v], 0, sizeof(ch[v]));
    39                     id[v]=0;
    40                 }
    41                 u=v;
    42             }
    43             if(!id[u]) id[u]=i; //error-prone: possibly duplicate patterns
    44             idx[i]=id[u];
    45         }
    46 
    47         for(int i=0; i<52; i++){
    48             int u=ch[0][i];
    49             if(u){
    50                 que.push(u);
    51                 fail[u]=last[u]=0;  //error-prone, must be initialized!!
    52             }
    53         }
    54 
    55         for(; que.size(); ){
    56             int u=que.front();
    57             que.pop();
    58             for(int i=0; i<52; i++){
    59                 //!view a variable (object) as an entity
    60                 int &v=ch[u][i];
    61                 if(v){  //v is a new node, construct a new node of AC automata
    62                     que.push(v);
    63                     //no need to init. last[] and fail[], as they are is induced.
    64                     fail[v]=ch[fail[u]][i];
    65                     last[v]=id[fail[v]]?fail[v]:last[fail[v]];
    66                 }
    67                 else{   //the expected node v does not exist
    68                     v=ch[fail[u]][i];
    69                 }
    70             }
    71         }
    72 
    73         for(int i=0, u=0; s[i]; i++){
    74             u=ch[u][get_id(s[i])];
    75             res[id[u]]=true;
    76             for(int v=last[u]; v; res[id[v]]=true, v=last[v]);  //error-prone
    77         }
    78 
    79         for(int i=1; i<=q; i++)
    80             puts(res[idx[i]]?"y":"n");
    81         
    82     }
    83 }

     UPD

     上面代码中第76行

     for(int v=last[u]; v; res[id[v]]=true, v=last[v]); 

    可优化为

     for(int v=last[u]; v && !res[id[v]]; res[id[v]]=true, v=last[v]); 

    这样可保证每个单词节点(dictionary node)通过last指针(dictionary suffix link) 的访问次数为至多为1 ,从而提高时间效率。


    上穷碧落下黄泉.

  • 相关阅读:
    2.NET Core设定数据库种子
    1.ASP.NET Core 中向 Razor Pages 应用添加模型
    获取文件夹目录下的文件信息
    dataGridView读写文本
    C# winform 启动外部程序
    netcore访问本地磁盘
    c#利用定时器自动备份数据库(mysql)
    c#mysql数据库备份还原
    Linux之旅(二)
    Linux之旅
  • 原文地址:https://www.cnblogs.com/Patt/p/5816531.html
Copyright © 2011-2022 走看看