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 ,从而提高时间效率。


上穷碧落下黄泉.

原文地址:https://www.cnblogs.com/Patt/p/5816531.html