AC自动机及KMP练习

好久都没敲过KMP和AC自动机了。以前只会敲个kuangbin牌板子套题。现在重新写了自己的板子加深了印象。并且刷了一些题来增加自己的理解。

KMP网上教程很多,但我的建议还是先看AC自动机(Trie图)的构造后再去理解。板子的话大家大同小异。

而AC自动机的构造则是推荐王贇的《Trie图的构建、活用与改进》

前面的备用知识则是字典树。推荐董华星的《浅析字母树在信息学竞赛中的应用》。董聚聚不仅仅是介绍了字典树,包括一些常见的应用也有论述,介绍的挺详细的。

接下来就是刷题的部分了。

hdu 5880

Family View

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2660    Accepted Submission(s): 577

Problem Description

Steam is a digital distribution platform developed by Valve Corporation offering digital rights management (DRM), multiplayer gaming and social networking services. A family view can help you to prevent your children access to some content which are not suitable for them.

Take an MMORPG game as an example, given a sentence T, and a list of forbidden words {P}, your job is to use ‘*‘ to subsititute all the characters, which is a part of the substring matched with at least one forbidden word in the list (case-insensitive).

For example, T is: "I love Beijing‘s Tiananmen, the sun rises over Tiananmen. Our great leader Chairman Mao, he leades us marching on."

And {P} is: {"tiananmen", "eat"}

The result should be: "I love Beijing‘s *********, the sun rises over *********. Our gr*** leader Chairman Mao, he leades us marching on."

Input

The first line contains the number of test cases. For each test case:
The first line contains an integer n

, represneting the size of the forbidden words list P

. Each line of the next n

lines contains a forbidden words Pi (1≤|Pi|≤1000000,∑|Pi|≤1000000)

where Pi

only contains lowercase letters.

The last line contains a string T (|T|≤1000000)

.

Output

For each case output the sentence in a line.

Sample Input

1
3
trump
ri
o
Donald John Trump (born June 14, 1946) is an American businessman, television personality, author, politician, and the Republican Party nominee for President of the United States in the 2016 election. He is chairman of The Trump Organization, which is the principal holding company for his real estate ventures and other business interests.

Sample Output

D*nald J*hn ***** (b*rn June 14, 1946) is an Ame**can businessman, televisi*n pers*nality, auth*r, p*litician, and the Republican Party n*minee f*r President *f the United States in the 2016 electi*n. He is chairman *f The ***** *rganizati*n, which is the p**ncipal h*lding c*mpany f*r his real estate ventures and *ther business interests.

Source

2016 ACM/ICPC Asia Regional Qingdao Online


裸题,把模式串构成Trie图然后在主串中匹配。Trie图中设置标记tag,若本身就是词尾节点则tag指向自己,否则指向该节点离他最近的词尾后缀节点。没有则指向根节点。接下来就转化为线段覆盖问题了。扫主串的时候扫到模式串对应的词头位置pos++,词尾pos--。接着求字符串pos前缀和,前缀和>0的部分赋’*’,然后输出字符串。

有两个版本,一个是刚开始按KMP的想法写的,稍微改改就能改成孩子兄弟链法:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<queue>
  5 #define clr(x) memset(x,0,sizeof(x))
  6 #define clr_1(x) memset(x,-1,sizeof(x))
  7 #define INF 0x3f3f3f3f
  8 #define mod 1000000007
  9 #define LL long long
 10 #define next nexted
 11 using namespace std;
 12 const int N=1e6+100;
 13 const int type=26;
 14 struct node
 15 {
 16     int pre;
 17     int dep;
 18     int tag;
 19     int next[type];
 20 }trie[N];
 21 int tot;
 22 int pos[N];
 23 int newnode()
 24 {
 25    trie[++tot]=(node){};
 26     return tot;
 27 }
 28 void add(int root,char *s)
 29 {
 30     int len=strlen(s);
 31     int now=root;
 32     int p;
 33     for(int i=0;i<len;i++)
 34     {
 35         p=s[i]-‘a‘;
 36         if(!trie[now].next[p])
 37         {
 38             trie[now].next[p]=newnode();
 39         }
 40         now=trie[now].next[p];
 41         trie[now].dep=i+1;
 42     }
 43     trie[now].tag=now;
 44 }
 45 void init()
 46 {
 47     tot=0;
 48     trie[0]=(node){};
 49     clr(pos);
 50     return ;
 51 }
 52 void getfail()
 53 {
 54     queue<int> que;
 55     int now,nowto,j;
 56     for(int i=0;i<type;i++)
 57         if(trie[0].next[i])
 58             que.push(trie[0].next[i]);
 59     while(!que.empty())
 60     {
 61         now=que.front();
 62         que.pop();
 63         for(int i=0;i<type;i++)
 64         {
 65             nowto=trie[now].next[i];
 66             if(nowto)
 67             {
 68                 que.push(nowto);
 69                 j=trie[now].pre;
 70                 while(j && !trie[j].next[i])
 71                     j=trie[j].pre;
 72                 trie[nowto].pre=trie[j].next[i];
 73                 if(trie[trie[j].next[i]].tag && !trie[nowto].tag)
 74                     trie[nowto].tag=trie[trie[j].next[i]].tag;
 75             }
 76         }
 77     }
 78     return ;
 79 }
 80 void acm(char *s)
 81 {
 82     int j=0,p,tmp;
 83     int len=strlen(s);
 84     for(int i=0;i<len;i++)
 85     if((s[i]>=‘a‘ && s[i]<=‘z‘)||(s[i]>=‘A‘ && s[i]<=‘Z‘))
 86     {
 87         p=(s[i]>=‘a‘ && s[i]<=‘z‘)?s[i]-‘a‘:s[i]-‘A‘;
 88         while(j && !trie[j].next[p])
 89         {
 90             j=trie[j].pre;
 91         }
 92         j=trie[j].next[p];
 93         if(trie[j].tag)
 94         {
 95             pos[i+1]--;
 96             pos[i-trie[trie[j].tag].dep+1]++;
 97         }
 98     }
 99     else
100     {
101         j=0;
102         continue;
103     }
104     j=0;
105     for(int i=0;i<len;i++)
106     {
107         j+=pos[i];
108         if(j>0)
109             s[i]=‘*‘;
110     }
111     return ;
112 }
113 int T,n,m;
114 char s[N];
115 int main()
116 {
117     scanf("%d",&T);
118     while(T--)
119     {
120         init();
121         scanf("%d",&n);
122         gets(s);
123         for(int i=1;i<=n;i++)
124         {
125             gets(s);
126             add(0,s);
127         }
128         getfail();
129         gets(s);
130         acm(s);
131         printf("%s\n",s);
132     }
133     return 0;
134 }

然后是按照论文里面bfs的构造法写的:

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<queue>
  5 #define clr(x) memset(x,0,sizeof(x))
  6 #define clr_1(x) memset(x,-1,sizeof(x))
  7 #define INF 0x3f3f3f3f
  8 #define mod 1000000007
  9 #define LL long long
 10 #define next nexted
 11 using namespace std;
 12 const int N=1e6+100;
 13 const int type=26;
 14 struct node
 15 {
 16     int pre;
 17     int dep;
 18     int tag;
 19     int next[type];
 20 }trie[N];
 21 int tot;
 22 int pos[N];
 23 int newnode()
 24 {
 25    trie[++tot]=(node){};
 26     return tot;
 27 }
 28 void add(int root,char *s)
 29 {
 30     int len=strlen(s);
 31     int now=root;
 32     int p;
 33     for(int i=0;i<len;i++)
 34     {
 35         p=s[i]-‘a‘;
 36         if(!trie[now].next[p])
 37             trie[now].next[p]=newnode();
 38         now=trie[now].next[p];
 39         trie[now].dep=i+1;
 40     }
 41     trie[now].tag=now;
 42 }
 43 void init()
 44 {
 45     tot=0;
 46     trie[0]=(node){};
 47     clr(pos);
 48     return ;
 49 }
 50 void build()
 51 {
 52     queue<int> que;
 53     int now,nowto;
 54     for(int i=0;i<type;i++)
 55         if(trie[0].next[i])
 56             que.push(trie[0].next[i]);
 57     while(!que.empty())
 58     {
 59         now=que.front();
 60         que.pop();
 61         for(int i=0;i<type;i++)
 62         {
 63             nowto=trie[now].next[i];
 64             if(nowto)
 65             {
 66                 que.push(nowto);
 67                 trie[nowto].pre=trie[trie[now].pre].next[i];
 68                 if(trie[trie[nowto].pre].tag && !trie[nowto].tag)
 69                     trie[nowto].tag=trie[trie[nowto].pre].tag;
 70             }
 71             else
 72                 trie[now].next[i]=trie[trie[now].pre].next[i];
 73         }
 74     }
 75     return ;
 76 }
 77 void acm(char *s)
 78 {
 79     int now=0,p,tmp;
 80     int len=strlen(s);
 81     for(int i=0;i<len;i++)
 82     if((s[i]>=‘a‘ && s[i]<=‘z‘)||(s[i]>=‘A‘ && s[i]<=‘Z‘))
 83     {
 84         p=(s[i]>=‘a‘ && s[i]<=‘z‘)?s[i]-‘a‘:s[i]-‘A‘;
 85         now=trie[now].next[p];
 86         if(trie[now].tag)
 87         {
 88             pos[i+1]--;
 89             pos[i-trie[trie[now].tag].dep+1]++;
 90         }
 91     }
 92     else
 93     {
 94         now=0;
 95         continue;
 96     }
 97     now=0;
 98     for(int i=0;i<len;i++)
 99     {
100         now+=pos[i];
101         if(now>0)
102             s[i]=‘*‘;
103     }
104     return ;
105 }
106 int T,n,m;
107 char s[N];
108 int main()
109 {
110     scanf("%d",&T);
111     while(T--)
112     {
113         init();
114         scanf("%d",&n);
115         gets(s);
116         for(int i=1;i<=n;i++)
117         {
118             gets(s);
119             add(0,s);
120         }
121         build();
122         gets(s);
123         acm(s);
124         printf("%s\n",s);
125     }
126     return 0;
127 }

tips: 虽然题目给的模式串总长不超过106然而实际不超过5*105,trie数组可以不开那么大,或者你改成孩子兄弟链法也是可行的。因为你一旦开那么大就会MLE。

然而你们看我代码为什么就不超呢。。我也是弄了好久才明白。。原来杭电他测内存占用的映射机制,只有当你使用过这个变量才会计算他是占用内存的。。所以你不写memset()或者对trie数组写指针引用的话,就不会超内存。

bzoj 2938

2938: [Poi2000]病毒

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 1300  Solved: 647
[Submit][Status][Discuss]

Description

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:

例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。

任务:

请写一个程序:

l         读入病毒代码;

l         判断是否存在一个无限长的安全代码;

l         将结果输出

Input

第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

Output

你应在在文本文件WIN.OUT的第一行输出一个单词:

l         TAK——假如存在这样的代码;

l         NIE——如果不存在。

Sample Input

3

01

11

00000

Sample Output

NIE



论文题。把Trie图构造出来以后dfs跑他的安全图看有没有环就行。有环说明能在这个环里一直跑,构造的字符串能无限长,无环则必定在某个位置到达词尾节点结束。

 1 #include<bits/stdc++.h>
 2 #define clr(x) memset(x,0,sizeof(x))
 3 #define clr_1(x) memset(x,-1,sizeof(x))
 4 #define INF 0x3f3f3f3f
 5 #define mod 1000000007
 6 #define LL long long
 7 #define next nexted
 8 using namespace std;
 9 const int N=3e4+10;
10 const int type=2;
11 char s[N];
12 int n,m,T,tot;
13 int vis[N];
14 struct node
15 {
16     int pre,tag,dep,next[2];
17 }trie[N];
18 void init()
19 {
20     tot=0;
21     clr(trie);
22     clr(vis);
23     return ;
24 }
25 int makenode()
26 {
27     return ++tot;
28 }
29 void add(int root,char *s)
30 {
31     int now=root,len=strlen(s),p;
32     for(int i=0;i<len;i++)
33     {
34         p=s[i]-‘0‘;
35         if(!trie[now].next[p]) trie[now].next[p]=makenode();
36         now=trie[now].next[p];
37         trie[now].dep=i+1;
38     }
39     trie[now].tag=now;
40     return ;
41 }
42 void build()
43 {
44     queue<int> que;
45     for(int i=0;i<type;i++)
46         if(trie[0].next[i]) que.push(trie[0].next[i]);
47     int now,nowto;
48     while(!que.empty())
49     {
50         now=que.front();
51         que.pop();
52         for(int i=0;i<type;i++)
53         {
54             nowto=trie[now].next[i];
55             if(nowto)
56             {
57                 trie[nowto].pre=trie[trie[now].pre].next[i];
58                 que.push(nowto);
59                 if(trie[trie[nowto].pre].tag && !trie[nowto].tag)
60                     trie[nowto].tag=trie[trie[nowto].pre].tag;
61             }
62             else
63                 trie[now].next[i]=trie[trie[now].pre].next[i];
64         }
65     }
66     return ;
67 }
68 bool dfs(int now)
69 {
70     if(trie[now].tag || vis[now]==2) return 0;
71     if(vis[now]==1) return 1;
72     vis[now]=1;
73     bool inf=0;
74     for(int i=0;i<type;i++)
75         inf=(inf || dfs(trie[now].next[i]));
76     vis[now]=2;
77     return inf;
78 }
79 int main()
80 {
81     scanf("%d",&n);
82     init();
83     for(int i=1;i<=n;i++)
84     {
85         scanf("%s",s);
86         add(0,s);
87     }
88     build();
89     if(dfs(0))
90         printf("TAK\n");
91     else
92         printf("NIE\n");
93     return 0;
94 }

原文地址:https://www.cnblogs.com/wujiechao/p/8675612.html

时间: 2024-10-05 15:52:15

AC自动机及KMP练习的相关文章

2017多校第8场 HDU 6138 Fleet of the Eternal Throne AC自动机或者KMP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6138 题意:给n个串,每次询问x号串和y号串的最长公共子串的长度,这个子串必须是n个串中某个串的前缀 解法1:AC自动机.做法是把n个串建成AC自动机,前缀树中每个节点都当做结尾节点,val赋为trie树深度,然后把x串丢进自动机里,把匹配到的前缀节点染个色,再把y串丢进去,遇到同样颜色的前缀节点就更新一下答案. #include <bits/stdc++.h> using namespace s

AC自动机——看似KMP在跑,其实fail在跳

先存代码 AC自动机(简单版) #include<bits/stdc++.h> #define maxn 1000007 using namespace std; int n,ans; int tr[maxn][28],val[maxn],cnt,fail[maxn]; char mod[maxn],tx[maxn]; queue<int >q; void build(char *a){ int now=0; for(int i=0;a[i];i++){ if(!tr[now][a

AC自动机--summer-work之我连模板题都做不出

这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目前我看过的文章: 深入理解Aho-Corasick自动机算法 AC 自动机学习笔记 AC自动机相比Trie多了失配边,结点到结点间的状态转移,结点到根的状态转移. 这里fail的定义是:使当前字符失配时跳转到另一段从root开始每一个字符都与当前已匹配字符段某一个后缀完全相同且长度最大的位置继续匹配

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

woj1572 Cyy and Fzz KMP / AC自动机 + DP

题目:http://acm.whu.edu.cn/land/problem/detail?problem_id=1572 题意:  有n个目标串,长度均小于15,(n<=8),现在随机写一个长度为L的字符串,问写下的这个字符串包含目标串的期望的个数. 比赛的时候还以为是水题,其实是自己太水.这种题一般是AC自动机的中等题,本题也可以用KMP做,结合状压dp. 方法一:AC自动机 建完Trie树后,就是跑一遍dp,注意单词节点要 |=(1<<val),会有重的字符串. dp过程: 用 dp

KMP,Trie,AC自动机题目集

字符串算法并不多,KMP,trie,AC自动机就是其中几个最经典的.字符串的题目灵活多变也有许多套路,需要多做题才能体会.这里收集了许多前辈的题目做个集合,方便自己回忆. KMP题目:https://blog.csdn.net/qq_38891827/article/details/80501506 Trie树题目:https://blog.csdn.net/qq_38891827/article/details/80532462 AC自动机:模板https://www.luogu.org/bl

字符串匹配相关模板(字典树、KMP、AC自动机)

字典树 struct Trie { int ch[MAXN][26]; int cnt; Trie() { cnt=1; memset(ch[0],0,sizeof(ch[0])); } int idx(char c) { return c-'a'; } void insert(char *s,int v) { int u=0,len=strlen(s); for (int i=0;i<len;i++) { int c=idx(s[i]); if (!ch[u][c]) { memset(ch[

hdu2222 Keywords Search &amp; AC自动机学习小结

传送门:http://http://acm.hdu.edu.cn/showproblem.php?pid=2222 思路:AC自动机入门题,直接上AC自动机即可. 对于构建AC自动机,我们要做的只有三件事: 1)构建字典树 2)构建失败指针 3)构建trie图(这道题好像不做这一步也能A...但是这一步不做是会被卡成O(n^2)的...) 1)第一步还是比较好理解的 根是虚根,边代表字母,那么根到终止节点的路径就是一个字符串,这样对于前缀相同的字符串我们就可以省下存公共前缀的空间. 加入一个模式

从Trie谈到AC自动机

ZJOI的SAM让我深受打击,WJZ大神怒D陈老师之T3是SAM裸题orz...我还怎么混?暂且写篇`从Trie谈到AC自动机`骗骗经验. Trie Trie是一种好玩的数据结构.它的每个结点存的是字母,因此得名`字母树`. 出一张图让大家感受下. (image powered by SaiBu NaoCu) 上面那是一棵插入了 ape,app,applicant,application,bake,ban,banana 等词的Trie.红色结点表示接受态. 显然,查找时只需顺着链照下来,插入只需