字符串BZOJ上面几个AC自动机求最为字串出现次数的题目
Posted hua-dong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符串BZOJ上面几个AC自动机求最为字串出现次数的题目相关的知识,希望对你有一定的参考价值。
(一下只供自己复习用,目的是对比这几个题,所以写得不详细。需要细节的可以参考其他博主)
【BZOJ3172:单词】
题目:
某人读论文,一篇论文是由许多(N)单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。N<=200,总单词长度不超过10^6。
思路:
简单题,建立AC自动机,插入的时候每个位置都++,代表以当前位置为后缀的字符串的个数,用于fail转移时累加。然后build得到fail指针;最后从叶子向根累加。
#include<bits/stdc++.h> using namespace std; const int maxn=1000010; char c[maxn]; int ans[maxn],pos[210],N; struct Trie { int ch[maxn][26],cnt,times,fail[maxn],q[maxn],head,tail; Trie(){ cnt=times=head=tail=0; } int insert(){ int Now=0,L=strlen(c+1); for(int i=1;i<=L;i++){ if(!ch[Now][c[i]-‘a‘]) ch[Now][c[i]-‘a‘]=++cnt; Now=ch[Now][c[i]-‘a‘]; ans[Now]++; } return Now; } void build() { for(int i=0;i<26;i++) if(ch[0][i]) q[++head]=ch[0][i]; while(tail<head){ int Now=q[++tail]; for(int i=0;i<26;i++){ if(ch[Now][i]){ fail[ch[Now][i]]=ch[fail[Now]][i]; q[++head]=ch[Now][i]; } else ch[Now][i]=ch[fail[Now]][i]; } } for(int i=tail;i>=1;i--) ans[fail[q[i]]]+=ans[q[i]]; } }T; int main() { scanf("%d",&N); for(int i=1;i<=N;i++){ scanf("%s",c+1); pos[i]=T.insert(); } T.build(); for(int i=1;i<=N;i++) printf("%d\n",ans[pos[i]]); return 0; }
【BZOJ2434阿狸的打字机】:
题目:
给定N个字符串。现在又Q个问题,每次问题给出(i,j),求第i个字符串在第j个字符串里出现的次数。 1<=N<=10e5;1<=M<=10e5;输入总长<=10e5
思路:
因为上一题是单次讯问,而且是整体求,所以一次拓扑倒序累加即可,但是此题是多次询问,而且是针对Trie树上代表的两个字符串之间的包含次数,不能整体法。 正解: 1,先建立AC自动机;2,得到fail树;3,对fail树进行DFS得到DFS序;4,在Trie树上dfs求解。
【BZOJ3881】
题意:
Bob有个字符串集合T,一开始为空,现在有两种操作:1,Bob的集合新增加一个字符串str; 2,Alice给出字符串x,问集合T中多少字符串包含x。1e6级别。
以上是关于字符串BZOJ上面几个AC自动机求最为字串出现次数的题目的主要内容,如果未能解决你的问题,请参考以下文章
LA_4670_Dominating_Patterns_(AC自动机+map)