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
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."
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)
.
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.
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define INF 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define next nexted
using namespace std;
const int N=1e6+;
const int type=;
struct node
{
int pre;
int dep;
int tag;
int next[type];
}trie[N];
int tot;
int pos[N];
int newnode()
{
trie[++tot]=(node){};
return tot;
}
void add(int root,char *s)
{
int len=strlen(s);
int now=root;
int p;
for(int i=;i<len;i++)
{
p=s[i]-'a';
if(!trie[now].next[p])
{
trie[now].next[p]=newnode();
}
now=trie[now].next[p];
trie[now].dep=i+;
}
trie[now].tag=now;
}
void init()
{
tot=;
trie[]=(node){};
clr(pos);
return ;
}
void getfail()
{
queue<int> que;
int now,nowto,j;
for(int i=;i<type;i++)
if(trie[].next[i])
que.push(trie[].next[i]);
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(nowto)
{
que.push(nowto);
j=trie[now].pre;
while(j && !trie[j].next[i])
j=trie[j].pre;
trie[nowto].pre=trie[j].next[i];
if(trie[trie[j].next[i]].tag && !trie[nowto].tag)
trie[nowto].tag=trie[trie[j].next[i]].tag;
}
}
}
return ;
}
void acm(char *s)
{
int j=,p,tmp;
int len=strlen(s);
for(int i=;i<len;i++)
if((s[i]>='a' && s[i]<='z')||(s[i]>='A' && s[i]<='Z'))
{
p=(s[i]>='a' && s[i]<='z')?s[i]-'a':s[i]-'A';
while(j && !trie[j].next[p])
{
j=trie[j].pre;
}
j=trie[j].next[p];
if(trie[j].tag)
{
pos[i+]--;
pos[i-trie[trie[j].tag].dep+]++;
}
}
else
{
j=;
continue;
}
j=;
for(int i=;i<len;i++)
{
j+=pos[i];
if(j>)
s[i]='*';
}
return ;
}
int T,n,m;
char s[N];
int main()
{
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
gets(s);
for(int i=;i<=n;i++)
{
gets(s);
add(,s);
}
getfail();
gets(s);
acm(s);
printf("%s\n",s);
}
return ;
}
然后是按照论文里面bfs的构造法写的:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define INF 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define next nexted
using namespace std;
const int N=1e6+;
const int type=;
struct node
{
int pre;
int dep;
int tag;
int next[type];
}trie[N];
int tot;
int pos[N];
int newnode()
{
trie[++tot]=(node){};
return tot;
}
void add(int root,char *s)
{
int len=strlen(s);
int now=root;
int p;
for(int i=;i<len;i++)
{
p=s[i]-'a';
if(!trie[now].next[p])
trie[now].next[p]=newnode();
now=trie[now].next[p];
trie[now].dep=i+;
}
trie[now].tag=now;
}
void init()
{
tot=;
trie[]=(node){};
clr(pos);
return ;
}
void build()
{
queue<int> que;
int now,nowto;
for(int i=;i<type;i++)
if(trie[].next[i])
que.push(trie[].next[i]);
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(nowto)
{
que.push(nowto);
trie[nowto].pre=trie[trie[now].pre].next[i];
if(trie[trie[nowto].pre].tag && !trie[nowto].tag)
trie[nowto].tag=trie[trie[nowto].pre].tag;
}
else
trie[now].next[i]=trie[trie[now].pre].next[i];
}
}
return ;
}
void acm(char *s)
{
int now=,p,tmp;
int len=strlen(s);
for(int i=;i<len;i++)
if((s[i]>='a' && s[i]<='z')||(s[i]>='A' && s[i]<='Z'))
{
p=(s[i]>='a' && s[i]<='z')?s[i]-'a':s[i]-'A';
now=trie[now].next[p];
if(trie[now].tag)
{
pos[i+]--;
pos[i-trie[trie[now].tag].dep+]++;
}
}
else
{
now=;
continue;
}
now=;
for(int i=;i<len;i++)
{
now+=pos[i];
if(now>)
s[i]='*';
}
return ;
}
int T,n,m;
char s[N];
int main()
{
scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
gets(s);
for(int i=;i<=n;i++)
{
gets(s);
add(,s);
}
build();
gets(s);
acm(s);
printf("%s\n",s);
}
return ;
}
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
Input
Output
Sample Input
01
11
00000
Sample Output
论文题。把Trie图构造出来以后dfs跑他的安全图看有没有环就行。有环说明能在这个环里一直跑,构造的字符串能无限长,无环则必定在某个位置到达词尾节点结束。
#include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define INF 0x3f3f3f3f
#define mod 1000000007
#define LL long long
#define next nexted
using namespace std;
const int N=3e4+;
const int type=;
char s[N];
int n,m,T,tot;
int vis[N];
struct node
{
int pre,tag,dep,next[];
}trie[N];
void init()
{
tot=;
clr(trie);
clr(vis);
return ;
}
int makenode()
{
return ++tot;
}
void add(int root,char *s)
{
int now=root,len=strlen(s),p;
for(int i=;i<len;i++)
{
p=s[i]-'';
if(!trie[now].next[p]) trie[now].next[p]=makenode();
now=trie[now].next[p];
trie[now].dep=i+;
}
trie[now].tag=now;
return ;
}
void build()
{
queue<int> que;
for(int i=;i<type;i++)
if(trie[].next[i]) que.push(trie[].next[i]);
int now,nowto;
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(nowto)
{
trie[nowto].pre=trie[trie[now].pre].next[i];
que.push(nowto);
if(trie[trie[nowto].pre].tag && !trie[nowto].tag)
trie[nowto].tag=trie[trie[nowto].pre].tag;
}
else
trie[now].next[i]=trie[trie[now].pre].next[i];
}
}
return ;
}
bool dfs(int now)
{
if(trie[now].tag || vis[now]==) return ;
if(vis[now]==) return ;
vis[now]=;
bool inf=;
for(int i=;i<type;i++)
inf=(inf || dfs(trie[now].next[i]));
vis[now]=;
return inf;
}
int main()
{
scanf("%d",&n);
init();
for(int i=;i<=n;i++)
{
scanf("%s",s);
add(,s);
}
build();
if(dfs())
printf("TAK\n");
else
printf("NIE\n");
return ;
}
bzoj 3670
3670: [Noi2014]动物园
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 3403 Solved: 1849
[Submit][Status][Discuss]
Description
近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。
某天,园长给动物们讲解KMP算法。
园长:“对于一个字符串S,它的长度为L。我们可以在O(L)的时间内,求出一个名为next的数组。有谁预习了next数组的含义吗?”
熊猫:“对于字符串S的前i个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作next[i]。”
园长:“非常好!那你能举个例子吗?”
熊猫:“例S为abcababc,则next[5]=2。因为S的前5个字符为abcab,ab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出next[1] = next[2] = next[3] = 0,next[4] = next[6] = 1,next[7] = 2,next[8] = 3。”
园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在O(L)的时间内求出next数组。
下课前,园长提出了一个问题:“KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。例如S为aaaaa,则num[4] = 2。这是因为S的前4个字符为aaaa,其中a和aa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,num[1] = 0,num[2] = num[3] = 1,num[5] = 2。”
最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出num数组呢?
特别地,为了避免大量的输出,你不需要输出num[i]分别是多少,你只需要输出对1,000,000,007取模的结果即可。
Input
第1行仅包含一个正整数n ,表示测试数据的组数。随后n行,每行描述一组测试数据。每组测试数据仅含有一个字符串S,S的定义详见题目描述。数据保证S 中仅含小写字母。输入文件中不会包含多余的空行,行末不会存在多余的空格。
Output
包含 n 行,每行描述一组测试数据的答案,答案的顺序应与输入数据的顺序保持一致。对于每组测试数据,仅需要输出一个整数,表示这组测试数据的答案对 1,000,000,007 取模的结果。输出文件中不应包含多余的空行。
Sample Input
aaaaa
ab
abcababc
Sample Output
1
32
HINT
n≤5,L≤1,000,000
这题是15年王鉴浩的论文题。对于本题我们对每个字符串算出next数组,并建立KMP自动机。然后dfs一遍KMP自动机,拿个数组存dfs经过点的编号。在节点u上,二分查找上述数组他所有小于等于u/2的祖先的个数,即为num[I]+1。
#include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define mod 1000000007
#define LL long long
using namespace std;
const int N=1e6+;
int next[N];
struct edg
{
int next,to;
}edge[N];
int head[N],etot;
void init()
{
etot=;
clr_1(head);
return ;
}
void addedge(int u,int v)
{
edge[++etot]=(edg){head[u],v};
head[u]=etot;
return ;
}
int que[N],ans[N];
char s[N];
int n,len,T,rans;
int getfail(int n,char *s)
{
int j=,len=strlen(s);
next[]=next[]=;
addedge(,);
for(int i=;i<len;i++)
{
while(j && s[i]!=s[j]) j=next[j];
if(s[i]==s[j]) j++;
next[i+]=j;
addedge(j,i+);
}
}
void dfs(int u,int dep)
{
ans[u]=upper_bound(que+,que+dep,u/)-que;
ans[u]--;
que[dep]=u;
for(int i=head[u];i!=-;i=edge[i].next)
dfs(edge[i].to,dep+);
return ;
}
int main()
{
scanf("%d",&T);
while(T--)
{
init();
scanf("%s",s);
len=strlen(s);
getfail(n,s);
dfs(,);
rans=;
for(int i=;i<=len;i++)
rans=((LL)ans[i]*rans)%mod;
printf("%d\n",rans);
} }
bzoj 3172
3172: [Tjoi2013]单词
Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 4761 Solved: 2333
[Submit][Status][Discuss]
Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
a
aa
aaa
Sample Output
3
1
HINT
Source
此题最朴素想法就是建一个trie图然后在把原文章在trie图上跑一遍,把跑到的每个节点cnt都++。然后在dfs一遍trie树回溯时把节点的cnt加到他的后缀指针节点的cnt上。不过据说会TLE我就没尝试了,毕竟再跑一遍trie图实在是花时间。于是我们想这题能不能把大头的时间去掉。我的选择就是在建立trie图的时候也在原文章上跑,省去了再跑一遍的时间。做法就是建立的时候经过的点cnt++。然后你dfs整个trie树也好,建立一个fail树也好。都是要把所有节点dfs一遍的233。我选择fail树,毕竟是fail树练手题。也就是每个单词出现的次数即其词尾的子树cnt之和。
#include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define mod 1000000007
#define LL long long
using namespace std;
const int N=1e6+;
const int M=2e2+;
const int type=;
char s[N];
int pt[M];
struct node
{
int next[type],tag,sum,suf;
}trie[N];
int tot;
int makenode()
{
++tot;
clr(&trie[tot]);
return tot;
}
void add(char *s,int num)
{
int now=,len=strlen(s),p;
for(int i=;i<len;i++)
{
p=s[i]-'a';
if(!trie[now].next[p])
trie[now].next[p]=makenode();
now=trie[now].next[p];
trie[now].sum++;
}
trie[now].tag=now;
pt[num]=now;
return ;
}
struct edg
{
int next,to;
}edge[N];
int head[N],etot;
void addedge(int u,int v)
{
edge[++etot]=(edg){head[u],v};
head[u]=etot;
return ;
}
void init()
{
tot=etot=;
clr_1(head);
return ;
}
void build()
{
int now,nowto;
queue<int> que;
for(int i=;i<type;i++)
if(trie[].next[i])
{
que.push(trie[].next[i]);
addedge(,trie[].next[i]);
}
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(nowto)
{
que.push(nowto);
trie[nowto].suf=trie[trie[now].suf].next[i];
addedge(trie[nowto].suf,nowto);
if(!trie[nowto].tag)
trie[nowto].tag=trie[trie[nowto].suf].tag;
}
else
trie[now].next[i]=trie[trie[now].suf].next[i];
}
}
return ;
}
void dfs(int u)
{
for(int i=head[u];i!=-;i=edge[i].next)
{
dfs(edge[i].to);
trie[u].sum+=trie[edge[i].to].sum;
}
return ;
}
int n,m,T;
int main()
{
init();
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%s",s);
add(s,i);
}
build();
dfs();
for(int i=;i<=n;i++)
printf("%d\n",trie[pt[i]].sum);
return ;
}
bzoj 2434
2434: [Noi2011]阿狸的打字机
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 3996 Solved: 2194
[Submit][Status][Discuss]
Description
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。
经阿狸研究发现,这个打字机是这样工作的:
l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。
l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。
l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
Input
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
Output
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
Sample Input
3
1 2
1 3
2 3
Sample Output
1
0
HINT
1<=N<=10^5
Source
#include<bits/stdc++.h>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define mod 1000000007
#define LL long long
#define time timed
using namespace std;
const int N=1e5+;
const int type=;
char s[N];
int pt[N];
struct node
{
int next[type],suf,fa;
}trie[N];
int tot,cnt,m;
int makenode()
{
++tot;
clr(&trie[tot]);
return tot;
}
struct edg
{
int next,to;
}edge[N],ques[N];
int head[N],qhead[N],qtot,etot;
int ans[N];
int u,v,time,fro[N],bac[N],len;
int bits[N];
void addedge(int u,int v)
{
edge[++etot].next=head[u];
edge[etot].to=v;
head[u]=etot;
return;
}
void addques(int u,int v)
{
ques[++qtot].next=qhead[u];
ques[qtot].to=v;
qhead[u]=qtot;
return;
}
void init()
{
cnt=tot=;
etot=qtot=;
time=;
clr_1(head);
clr_1(qhead);
clr(bits);
}
void create(char *s)
{
int now=,len=strlen(s),p;
for(int i=;i<len;i++)
{
if(s[i]=='B') now=trie[now].fa;
else if(s[i]=='P') pt[++cnt]=now;
else
{
p=s[i]-'a';
if(!trie[now].next[p])
{
trie[now].next[p]=makenode();
trie[trie[now].next[p]].fa=now;
}
now=trie[now].next[p];
}
}
}
void build()
{
queue<int> que;
int now,nowto;
for(int i=;i<type;i++)
if(trie[].next[i])
{
addedge(,trie[].next[i]);
que.push(trie[].next[i]);
}
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(nowto)
{
que.push(nowto);
trie[nowto].suf=trie[trie[now].suf].next[i];
addedge(trie[nowto].suf,nowto);
}
else
trie[now].next[i]=trie[trie[now].suf].next[i];
}
}
return ;
}
void dfs(int u)
{
fro[u]=++time;
for(int i=head[u];i!=-;i=edge[i].next) dfs(edge[i].to);
bac[u]=time;
return ;
}
void add(int x,int val)
{
while(x<=time)
{
bits[x]+=val;
x+=x&-x;
}
return ;
}
int getsum(int x)
{
int ans=;
while(x)
{
ans+=bits[x];
x-=x&-x;
}
return ans;
}
void getans(char *s)
{
int now=,len=strlen(s),p;
int ct=;
for(int i=;i<len;i++)
{
if(s[i]=='B')
{
add(fro[now],-);
now=trie[now].fa;
}
else if(s[i]=='P')
{
ct++;
for(int i=qhead[ct];i!=-;i=ques[i].next)
{
p=ques[i].to;
ans[i]=getsum(bac[pt[p]])-getsum(fro[pt[p]]-);
}
}
else
{
p=s[i]-'a';
now=trie[now].next[p];
add(fro[now],);
}
}
return ;
}
int main()
{
scanf("%s",s);
init();
create(s);
build();
scanf("%d",&m);
for(int i=;i<=m;i++)
{
scanf("%d%d",&u,&v);
addques(v,u);
}
dfs();
getans(s);
for(int i=;i<=m;i++)
printf("%d\n",ans[i]);
return ;
}
3881: [Coci2015]Divljak
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 1041 Solved: 340
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
a
bc
abc
5
1 abca
2 1
1 bca
2 2
2 3
Sample Output
2
1
HINT
#include<cstdio>
#include<iostream>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
#define clr(x) memset(x,0,sizeof(x))
#define clr_1(x) memset(x,-1,sizeof(x))
#define mod 1000000007
#define LL long long
using namespace std;
const int N=2e6+;
const int M=1e5+;
const int type=;
struct node{
int next[type],suf;
}trie[N];
int tcnt;
char s[N];
int pt[M];
queue<int> que;
stack<int> sta;
struct edg
{
int next,to;
}edge[N];
int head[N],ecnt;
int fa[N][],bit[];
int clk,fro[N],bac[N],dep[N];
int tot,ppt[N],vfa[N];
int bits[N];
void add(int x,int val)
{
while(x<=clk)
{
bits[x]+=val;
x+=x&-x;
}
return ;
}
int getsum(int x)
{
int ans=;
while(x)
{
ans+=bits[x];
x-=x&-x;
}
return ans;
}
inline int sum(int l,int r)
{
return getsum(r)-getsum(l-);
}
void init()
{
tcnt=;
ecnt=;
clr_1(head);
bit[]=;
for(int i=;i<;i++)
bit[i]=bit[i-]<<;
clk=;
return ;
}
int makenode()
{
++tcnt;
memset(&trie[tcnt],,sizeof(trie[tcnt]));
return tcnt;
}
void addedge(int u,int v)
{
edge[++ecnt].next=head[u];
edge[ecnt].to=v;
head[u]=ecnt;
return;
}
int insert(char *s)
{
int now=,p;
for(int i=;s[i];i++)
{
p=s[i]-'a';
if(!trie[now].next[p])
trie[now].next[p]=makenode();
now=trie[now].next[p];
}
return now;
}
void build()
{
int now=,nowto;
for(int i=;i<type;i++)
if(trie[now].next[i])
que.push(trie[now].next[i]);
while(!que.empty())
{
now=que.front();
que.pop();
for(int i=;i<type;i++)
{
nowto=trie[now].next[i];
if(!nowto)
trie[now].next[i]=trie[trie[now].suf].next[i];
else
que.push(nowto),trie[nowto].suf=trie[trie[now].suf].next[i];
}
}
for(int i=;i<=tcnt;i++)
addedge(trie[i].suf,i);
return ;
}
void dfs(int u,int deep,int f)
{
dep[u]=deep;
fa[u][]=f;
fro[u]=++clk;
for(int i=;bit[i]<=deep;i++) fa[u][i]=fa[fa[u][i-]][i-];
for(int i=head[u];~i;i=edge[i].next)
dfs(edge[i].to,deep+,u);
bac[u]=clk;
}
bool cmp(int a,int b)
{
return fro[a]<fro[b];
}
int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int tmp=dep[u]-dep[v];
for(int i=;bit[i]<=tmp;i++)
if(tmp&bit[i]) u=fa[u][i];
int i=;
while(bit[i]>dep[u]) i--;
for(;i>=;i--)
if(fa[u][i]!=fa[v][i]) {u=fa[u][i]; v=fa[v][i];}
return (u==v)?(u):(fa[u][]);
}
void ins(char *s)
{
int p,now=,x;
tot=;
ppt[++tot]=;
for(int i=;s[i];i++)
{
p=s[i]-'a';
now=trie[now].next[p];
ppt[++tot]=now;
}
sort(ppt+,ppt+tot+,cmp);
tot=unique(ppt+,ppt+tot+)-ppt-;
for(int i=,nowtot=tot;i<=nowtot;i++)
{
if(sta.empty()) {sta.push(ppt[i]);vfa[ppt[i]]=-;continue;}
p=lca(ppt[i],sta.top());
while(!sta.empty() && dep[sta.top()]>dep[p])
{
x=sta.top();
sta.pop();
if((!sta.empty()&& dep[sta.top()]<=dep[p]) || sta.empty())
vfa[x]=p;
}
if(!sta.empty() && sta.top()!=p)
{
vfa[p]=sta.top();
sta.push(p);
ppt[++tot]=p;
}
else if(sta.empty())
{
vfa[p]=-;
sta.push(p);
ppt[++tot]=p;
}
vfa[ppt[i]]=p; sta.push(ppt[i]);
}
for(int i=;i<=tot;i++)
{
if(vfa[ppt[i]]==-) continue;
add(fro[ppt[i]],);
add(fro[vfa[ppt[i]]],-);
}
while(!sta.empty())
sta.pop();
return ;
}
int main()
{
int n,m,op,ques;
init();
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%s",s);
pt[i]=insert(s);
}
build();
dfs(,,);
scanf("%d",&m);
for(int i=;i<=m;i++)
{
scanf("%d",&op);
if(op==)
{
scanf("%s",s);
ins(s);
}
else
{
scanf("%d",&ques);
printf("%d\n",sum(fro[pt[ques]],bac[pt[ques]]));
}
}
return ;
}
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个串中某个串的前缀 ...
- AC自动机——看似KMP在跑,其实fail在跳
先存代码 AC自动机(简单版) #include<bits/stdc++.h> #define maxn 1000007 using namespace std; int n,ans; i ...
- AC自动机
AC自动机,全称Aho-Corasick自动机.如果没记错的话好像就是前缀自动机. 其实AC自动机就是KMP上树的产物.理解了KMP,那AC自动机应该也是很好理解的. 与KMP类似,AC自动机也是扔一 ...
- AC 自动机
AC自动机(Aho-Corasick Automata)是经典的多模式匹配算法.从前我学过这个算法,但理解的不深刻,现在已经十分不明了了.现在发觉自己对大部分算法的掌握都有问题,决定重写一系列博客把学 ...
- AC自动机讲解
今天花了半天肝下AC自动机,总算啃下一块硬骨头,熬夜把博客赶出来.. 正如许多博客所说,AC自动机看似很难很妙,而事实上不难,但的确很妙.笼统地说,AC自动机=Trie+KMP,但是仅仅知道这个并没有 ...
- [AC自动机][学习笔记]
用途 AC自动机适用于一类用多个子串在模板串中匹配的字符串问题. 也就是说先给出一个模板串,然后给出一些子串.要求有多少个子串在这个模板串中出现过. KMP与trie树 其实AC自动机就是KMP与tr ...
- 【模板】AC自动机(简单版)
我:“woc...AC自动机?” 我:“可以自动AC???” 然鹅... 大佬:“傻...” 我:“(⊙_⊙)?” 大佬:“缺...” 我:“......” (大佬...卒 | 逃...) emm.. ...
- HDU 2222 Keywords Search(AC自动机)题解
题意:给你几个keywords,再给你一段文章,问你keywords出现了几次. 思路:这里就要用到多模匹配算法AC自动机了,AC自动机需要KMP和字典树的知识,匹配时是在字典树上,失配我们就要用到类 ...
- bzoj 2754 ac自动机
第一道AC自动机题目. 记一下对AC自动机的理解吧: AC自动机=Trie+KMP.即在Trie上应用KMP思想,实现多Pattern的匹配问题. 复杂度是预处理O(segma len(P)),匹配是 ...
随机推荐
- CSS浮动为什么不会遮盖同级元素
1.问题描述 在W3CSchool学习web前端时,看完CSS定位-浮动这一节后,感觉没有什么问题.但是在CSS高级-分类这一节的中进行实践时,遇到了如下问题.测试地址:浮动的简单应用. 完整的htm ...
- CART算法(转)
来源:http://www.cnblogs.com/pinard/p/6053344.html 作者:刘建平Pinard 对于C4.5算法,我们也提到了它的不足,比如模型是用较为复杂的熵来度量,使用了 ...
- c json实战引擎五 , 优化重构
引言 scjson是一个小巧的纯c跨平台小巧引擎. 适用于替换老的cJSON引擎的场景. 数据结构和代码布局做了大量改进.优势体现在以下几个方面: 1) 跨平台 (window 10 + VS2017 ...
- 解决Mac开机变慢 command +option + P + R
Mac开机变慢怎么办? command +option + P + R 重点是 开机 后 一直按 该4个键不放 听到3声音响 屏幕出现灰暗灰暗几次 开机速度 5s 重置PRAM和NVRAM的方法都是 ...
- Makefile系列之一 : 书写规则
1. 规则 target : prerequisites command 2. example excute 为最终生成的可执行文件. 可以通过命令 make clean来删除所有编译时产生的中间文 ...
- awk常见操作整理(更新)
awk的基本结构 awk 'BEGIN{} pattern {} END {}' #pattern {} 部分是针对每行进行循环处理的,有pattern表示对匹配到的行处理,没有pattern表示对所 ...
- FineReport——发送邮件
在FR中,个用户之间可以通过邮件的形式进行通信,但是存在一个问题就是FineReport平台只能设置一个发件人账户,收件人账户可以有多个. 所以在实际的系统开发中可能需要自己开发信息交互的模块. 在系 ...
- Content to Node: Self-Translation Network Embedding
paper:https://dl.acm.org/citation.cfm?id=3219988 data & code:http://dm.nankai.edu.cn/code/STNE.r ...
- 查询 IDE 的版本 方便安装第三方的时候选择
TMD 很多第三方软件 的版本 定义 与 delphi是不一致的. 1.有的是以这个为准.
- hive学习(三) hive的分区
1.Hive 分区partition 必须在表定义时指定对应的partition字段 a.单分区建表语句: create table day_table (id int, content string ...