190709-sugar-AC自动机/dp

夙愿已清 提交于 2019-12-01 07:13:17
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<string>
 4 #include<cstring>
 5 #include<algorithm>
 6 using namespace std;
 7 namespace Moxing{
 8     const int N=5e6+50,inf=0x3f3f3f3f;
 9     char b[N],s[N];
10     int c[N][2],fail[N],q[N],mi[N],cnt,l,r,len,a[N],f[N],n;
11     void insert(char *s){
12         int len=strlen(s),now=0;
13         for(int i=0;i<len;i++){
14             int x=s[i]-'0';
15             if(!c[now][x]) c[now][x]=++cnt;
16             now=c[now][x];
17         }
18         mi[now]=len;
19     }
20     void build(){
21         for(int i=0;i<2;i++){
22             if(c[0][i]) q[++r]=c[0][i];
23         }
24         l=1;
25         while(l<=r){
26             int x=q[l++];
27             for(int i=0;i<2;i++){
28                 int &y=c[x][i];
29                 if(!y) y=c[fail[x]][i];
30                 else fail[y]=c[fail[x]][i],q[++r]=y,mi[y]=min(mi[y],mi[c[fail[x]][i]]);
31             }
32         }
33     } 
34     void ask(char *s){
35         int now=0;len=strlen(s+1);
36         for(int i=1;i<=len;i++){
37             int x=s[i]-'0';
38             now=c[now][x];a[i]=mi[now];
39         } 
40     }
41     struct main{
42         main(){
43             memset(mi,0x3f,sizeof(mi));
44             scanf("%d%s",&n,b+1);
45             for(int i=1;i<=n;i++){
46                 scanf("%s",s),insert(s);
47             }
48             build(),ask(b);
49             for(int i=1;i<=len;i++){
50                 f[i]=f[i-1];
51                 if(a[i]!=inf) f[i]=max(f[i],f[i-a[i]]+1); 
52             }
53             printf("%d",f[len]);
54             exit(0);
55         }
56     }UniversalLove;
57 }
58 int main(){
59     Moxing::main();
60 }
61 //5
62 //010101010111
63 //101
64 //01
65 //11
66 //1000
67 //1110
68 //mi数组记的是now=i时最小的len,a就是串上这个位置作为末尾能匹配到的最短的串的长度
69 //ask函数把now=i时最小的len转换到串的位置上
View Code

 

以下转自https://www.cnblogs.com/justPassBy/p/5412110.html

fail指针的性质:

每个结点的fail指针都指向自己的最长后缀,让一个结点cur的fail指针不断回溯向上走,直到碰到根结点为止,回溯时经过的结点所代表的字符串都是结点cur所代表的字符串的后缀。

Trie图和AC自动机的区别:

Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了。这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯。

比如构造next[cur][i]的fail指针,cur为父节点,next[cur][i]为cur的儿子结点,如果是AC自动机,如果父亲结点tmp(tmp是cur的一份拷贝)的next[fail[tmp]][i]不存在时,需要让tmp不断回溯(即tmp = fail[tmp]),直到next[fail[tmp]][i]不为空时,才让fail[next[cur][i]] = next[fail[tmp]][i]。

如果是Trie图,那么直接让fail[next[cur][i]] = next[fail[cur]][i]就可以了,因为Trie图已经补全了next指针。

fail树:

Fail树其实就是将AC自动机的next指针去掉,然后反转fail指针的指向所构造出来的,而且可以肯定这是一棵树 ,所以称之为Fail树。

Fail树的一个性质是,某个结点所对应的字符串肯定是其儿子结点,孙子结点…所对应的字符串的后缀。

Trie图和AC自动机的区别

Trie图是AC自动机的确定化形式,即把每个结点不存在字符的next指针都补全了。这样做的好处是使得构造fail指针时不需要next指针为空而需要不断回溯。

比如构造next[cur][i]的fail指针,cur为父节点,next[cur][i]为cur的儿子结点,如果是AC自动机,如果父亲结点tmp(tmp是cur的一份拷贝)的next[fail[tmp]][i]不存在时,需要让tmp不断回溯(即tmp = fail[tmp]),直到next[fail[tmp]][i]不为空时,才让fail[next[cur][i]] = next[fail[tmp]][i]。

如果是Trie图,那么直接让fail[next[cur][i]] = next[fail[cur]][i]就可以了,因为Trie图已经补全了next指针。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!