以前KMP和后缀系列(主要是后缀数组,后缀自动机),都刷了一定数量的题,但是对于AC自动机,却有些冷落,罪过。

但是我感觉,在蓝桥杯比赛中AC自动机出现的概率比后缀系列大,简单的会考匹配,稍难一点会考AC自动机+DP ,AC自动机+矩阵乘法,或者套其他算法blabla...

Trie图是AC自动机的改良版,不需要一直向上找fail。然后这里整理了一下Trie图的模板。

HihoCoder1036:Trie图  (时间在hihocoder上面排第一)。

题意:问长字符串里是否出现过字典里的单词。
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
char s[maxn];
int ch[maxn][],num[maxn],Next[maxn];
int q[maxn],head,tail,cnt;
struct ACauto
{
void insert()
{
int Now=;
for(int i=;s[i];i++){
if(!ch[Now][s[i]-'a']) ch[Now][s[i]-'a']=++cnt;
Now=ch[Now][s[i]-'a'];
} num[Now]=;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]){
Next[ch[][i]]=;
q[++head]=ch[][i];
//if(num[Next[ch[0][i]]]) num[ch[0][i]]=1;
}
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
//if(num[Next[ch[u][i]]]) num[ch[u][i]]=1;
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
bool find()
{
scanf("%s",s);
int Now=;
for(int i=;s[i];i++){
Now=ch[Now][s[i]-'a'];
if(num[Now]) return true;
} return false;
}
}Trie;
int main()
{
int N,i,j;
scanf("%d",&N);
for(i=;i<=N;i++){
scanf("%s",s);
Trie.insert();
}
Trie.build();
if(Trie.find()) printf("YES\n");
else printf("NO\n");
return ;
}

HDU2222 :Keywords Search(基础题型)

题意:问长字符串里出现了多少个字典里的单词。(字典里的单词可能重复,但是一个单词只统计一次)
思路:AC自动机统计以当前字母为后缀的单词数,并且沿fail指针一直统计,统计过后把num改为-,避免重复统计。
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
char s[maxn];
int ch[maxn][],num[maxn],Next[maxn];
int q[maxn],head,tail,cnt,ans;
struct ACauto
{
void update()
{
head=tail=cnt=ans=;
memset(ch,,sizeof(ch));
memset(num,,sizeof(num));
memset(Next,,sizeof(Next));
}
void insert()
{
int Now=;
for(int i=;s[i];i++){
if(!ch[Now][s[i]-'a']) ch[Now][s[i]-'a']=++cnt;
Now=ch[Now][s[i]-'a'];
} num[Now]++;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]){
Next[ch[][i]]=;
q[++head]=ch[][i];
}
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void find()
{
scanf("%s",s);
int Now=;
for(int i=;s[i];i++){
Now=ch[Now][s[i]-'a'];
int tmp=Now;
while(tmp&&num[tmp]!=-){
ans+=num[tmp]; num[tmp]=-;
tmp=Next[tmp];
}
} return ;
}
}Trie;
int main()
{
int T,N,i,j;
scanf("%d",&T);
while(T--){
Trie.update();
scanf("%d",&N);
for(i=;i<=N;i++){
scanf("%s",s);
Trie.insert();
}
Trie.build();
Trie.find();
printf("%d\n",ans);
}
return ;
}

HDU2896:病毒侵袭 (基础题型)

题意:给定N个病毒,M个网站,问对每个网站,带了哪些病毒,输出其编号。
思路:和上面差不多。注意memset会MTL。然后就是题目的ASCLL码范围的0到126。
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
char s[maxn];
int ch[maxn][],Next[maxn];
int q[maxn],num[maxn],head,tail,cnt,ans;
struct ACauto
{
void update()
{
head=tail=cnt=ans=;
num[]=Next[]=;
for(int i=;i<;i++) ch[][i]=;
}
void insert(int opt)
{
int Now=;
for(int i=;s[i];i++){
if(!ch[Now][s[i]]){
ch[Now][s[i]]=++cnt;
for(int i=;i<;i++) ch[cnt][i]=,num[cnt]=,Next[cnt]=;
}
Now=ch[Now][s[i]];
} num[Now]=opt;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]){
Next[ch[][i]]=;
q[++head]=ch[][i];
}
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void find(int opt)
{
scanf("%s",s);
int Now=,x[],size=;
for(int i=;s[i];i++){
Now=ch[Now][s[i]];
int tmp=Now;
while(tmp){
if(num[tmp]) x[++size]=num[tmp];
tmp=Next[tmp];
}
}
if(size){
ans++; sort(x+,x+size+);
printf("web %d: %d",opt,x[]);
for(int i=;i<=size;i++) printf(" %d",x[i]);
printf("\n");
} return ;
}
}Trie;
int main()
{
int N,Q,i,j;
while(~scanf("%d",&N)){
Trie.update();
for(i=;i<=N;i++){
scanf("%s",s);
Trie.insert(i);
}
Trie.build();
scanf("%d",&Q);
for(i=;i<=Q;i++) Trie.find(i);
printf("total: %d\n",ans);
}
return ;
}

但是上面那个需要一直沿fail指针上找,如果数据大一点就过不了。

改进是直接记录前缀‘和’,使不需要上找。

#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
char s[maxn];
int ch[maxn][],Next[maxn];
int q[maxn],e[maxn][],num[maxn],head,tail,cnt,ans;
struct ACauto
{
void update()
{
head=tail=cnt=ans=;
num[]=Next[]=;
for(int i=;i<;i++) ch[][i]=;
}
void insert(int opt)
{
int Now=;
for(int i=;s[i];i++){
if(!ch[Now][s[i]]){
ch[Now][s[i]]=++cnt;
for(int i=;i<;i++) ch[cnt][i]=,e[cnt][]=,Next[cnt]=;
}
Now=ch[Now][s[i]];
} e[Now][++e[Now][]]=opt;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]){
Next[ch[][i]]=;
q[++head]=ch[][i];
}
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
for(int j=;j<=e[ch[Next[u]][i]][];j++){//记录前缀‘和 ’
e[ch[u][i]][++e[ch[u][i]][]]=e[ch[Next[u]][i]][j];
}
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void find(int opt)
{
scanf("%s",s);
int Now=,x[],size=;
for(int i=;s[i];i++){
Now=ch[Now][s[i]];
int tmp=Now;
for(int j=;j<=e[Now][];j++) x[++size]=e[Now][j];
/*while(tmp){
if(num[tmp]) x[++size]=num[tmp];
tmp=Next[tmp];
}*/
}
if(size){
ans++; sort(x+,x+size+);
printf("web %d: %d",opt,x[]);
for(int i=;i<=size;i++) printf(" %d",x[i]);
printf("\n");
} return ;
}
}Trie;
int main()
{
int N,Q,i,j;
while(~scanf("%d",&N)){
Trie.update();
for(i=;i<=N;i++){
scanf("%s",s);
Trie.insert(i);
}
Trie.build();
scanf("%d",&Q);
for(i=;i<=Q;i++) Trie.find(i);
printf("total: %d\n",ans);
}
return ;
}

HDU3065:病毒侵袭持续中 (基础题型)

题意:求每个单词在字符串里出现次数。
思路:和上一题差不多,记录路过单词的尾节点End的次数即可。字符串里不是大写字母的要回到根。
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
char s[][],x[];
int ch[maxn][],Next[maxn];
int q[maxn],End[maxn],num[],head,tail,cnt,ans;
struct ACauto
{
void update()
{
memset(num,,sizeof(num));
head=tail=cnt=ans=Next[]=;
for(int i=;i<;i++) ch[][i]=;
}
void insert(int opt)
{
int Now=;
for(int i=;s[opt][i];i++){
if(!ch[Now][s[opt][i]-'A']){
ch[Now][s[opt][i]-'A']=++cnt;
Next[cnt]=End[cnt]=;
for(int i=;i<;i++) ch[cnt][i]=;
}
Now=ch[Now][s[opt][i]-'A'];
} End[Now]=opt;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]){
Next[ch[][i]]=;
q[++head]=ch[][i];
}
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void find()
{
scanf("%s",x); int Now=;
for(int i=;x[i];i++){
if(x[i]<'A'||x[i]>'Z') Now=;
else Now=ch[Now][x[i]-'A'];
if(End[Now]) num[End[Now]]++;
}
}
}Trie;
int main()
{
int N,Q,i,j;
while(~scanf("%d",&N)){
Trie.update();
for(i=;i<=N;i++){
scanf("%s",s[i]);
Trie.insert(i);
}
Trie.build();
Trie.find();
for(i=;i<=N;i++)
if(num[i]) {
printf("%s: %d\n",s[i],num[i]);
}
}
return ;
}

POJ2778: DNA Sequence (AC自动机+矩阵)

题意:给定几个病毒DNA,现在求有多少种长度位K的DNA,里面不含病毒DNA。
思路:路径(路径很长)种类一般都是需要矩阵优化的,这里把病毒DNA建立AC自动机,然后得到0可以到达的点(不形成病毒DNA)的和。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int Mod=;
const int maxn=;
int ch[maxn][],id[maxn],cnt;
int q[maxn],head,tail,Next[maxn],tag[maxn];
char s[]; ll ans;
struct mat
{
ll mp[maxn][maxn];
mat(){memset(mp,,sizeof(mp));}
mat friend operator *(mat a,mat b)
{
mat res;
for(int k=;k<=cnt;k++)
for(int i=;i<=cnt;i++)
for(int j=;j<=cnt;j++){
res.mp[i][j]=(res.mp[i][j]+a.mp[i][k]*b.mp[k][j])%Mod;
} return res;
}
mat friend operator ^(mat a,int x)
{
mat res; for(int i=;i<=cnt;i++) res.mp[i][i]=;
while(x){
if(x&) res=res*a;
a=a*a; x>>=;
} return res;
}
};
mat array;
struct ACautom
{
void insert()
{
int Now=;
for(int i=;s[i];i++){
int x=id[s[i]];
if(!ch[Now][x]) ch[Now][x]=++cnt;
Now=ch[Now][x];
} tag[Now]=;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]) q[++head]=ch[][i];
if(!tag[ch[][i]]) array.mp[][ch[][i]]++;
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
if(tag[Next[ch[u][i]]]) tag[ch[u][i]]=;
}
else ch[u][i]=ch[Next[u]][i];
if(!tag[ch[u][i]]) array.mp[u][ch[u][i]]++;
}
}
}
void qpow(int K)
{
array=array^K;
for(int i=;i<=cnt;i++) ans=(ans+array.mp[][i])%Mod;
printf("%lld\n",ans);
}
}Trie;
int main()
{
int N,K,i;
id['A']=; id['G']=; id['C']=; id['T']=;
scanf("%d%d",&N,&K);
for(i=;i<=N;i++) {
scanf("%s",s);
Trie.insert();
}
Trie.build();
Trie.qpow(K);
return ;
}

HDU2243:考研路茫茫——单词情结 (AC自动机+矩阵)

题意:问长度位1到L的所有单词中,有多少个不含给出的几个单词。
思路:和上一题差不多,但是要解决前缀和问题。有两种解决方案,一是二分;二是利用矩阵加一维,可以得到X^+X^+X^...X^N。
比如得到26的0到N次幂和,就有矩阵a[0][0]=26,a[0][1]=1,a[1][0]=0,a[1][1]=1; 矩阵^N后,第一行的和就是答案。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ull unsigned long long
const int maxn=;
int ch[maxn][],cnt;
int q[maxn],head,tail,Next[maxn],tag[maxn];
char s[];
struct mat
{
ull mp[maxn][maxn];
mat(){memset(mp,,sizeof(mp));}
mat init(){ memset(mp,,sizeof(mp));}
mat friend operator *(mat a,mat b)
{
mat res;
for(int k=;k<=max(cnt,);k++)
for(int i=;i<=max(cnt,);i++)
for(int j=;j<=max(cnt,);j++)
res.mp[i][j]+=a.mp[i][k]*b.mp[k][j];
return res;
}
mat friend operator ^(mat a,int x)
{
mat res;
for(int i=;i<=cnt;i++)
res.mp[i][i]=;
while(x){
if(x&) res=res*a;
a=a*a; x>>=;
} return res;
}
}; mat array; struct ACautom
{
void update()
{
cnt=head=tail=;
memset(Next,,sizeof(Next));
memset(tag,,sizeof(tag));
memset(ch,,sizeof(ch));
array.init();
}
void insert()
{
int Now=;
for(int i=;s[i];i++){
int x=s[i]-'a';
if(!ch[Now][x]) ch[Now][x]=++cnt;
Now=ch[Now][x];
} tag[Now]=;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i]) q[++head]=ch[][i];
if(!tag[ch[][i]]) array.mp[][ch[][i]]++;
}
while(tail<head){
int u=q[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
q[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
if(tag[Next[ch[u][i]]]) tag[ch[u][i]]=;
}
else ch[u][i]=ch[Next[u]][i];
if(!tag[ch[u][i]]) array.mp[u][ch[u][i]]++;
}
}
cnt++;
for(int i=;i<=cnt;i++) array.mp[i][cnt]=;
}
void qpow(int K)
{
ull ans,res=;
mat base;
base.mp[][]=; base.mp[][]=;
base.mp[][]=; base.mp[][]=;
base=base^K;
ans=base.mp[][]+base.mp[][];
array=array^K;
for(int i=;i<=cnt;i++) res=res+array.mp[][i];
cout<<ans-res<<endl;
}
}Trie;
int main()
{
int N,K;
while(~scanf("%d%d",&N,&K)){
Trie.update();
for(int i=;i<=N;i++) {
scanf("%s",s);
Trie.insert();
}
Trie.build();
Trie.qpow(K);
}
return ;
}

HDU2825:Wireless Password (AC自动机+状压DP)

题意:对于M个单词,求长度为N的字符串,至少包含了K个单词的数量。
思路:AC自动机+状压DP。注意有可能有相同的字符串,所以End处经常会用到"|="。
dp[L][X][K]表示长度为L时,在AC自动机上X节点,已经包含字符串情况为K的种类数。
复杂度25**<<。
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
const int Mod=;
int dp[][maxn][<<];
int Next[maxn],ch[maxn][],End[maxn],N,M,K,cnt;
int que[maxn],head,tail; char s[maxn];
struct ACauto
{
void update()
{
cnt=head=tail=;
memset(ch,,sizeof(ch));
memset(Next,,sizeof(Next));
memset(End,,sizeof(End));
memset(dp,,sizeof(dp));
}
void insert(int tag)
{
int Now=;
tag=<<tag;
for(int i=;s[i];i++){
if(!ch[Now][s[i]-'a']) ch[Now][s[i]-'a']=++cnt;
Now=ch[Now][s[i]-'a'];
} End[Now]|=tag;//不是等于,一位可能两个相同的单词
}
void build()
{
for(int i=;i<;i++){
if(ch[][i])
que[++head]=ch[][i];
}
while(tail<head){
int u=que[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
que[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
End[ch[u][i]]|=End[Next[ch[u][i]]];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
bool count(int x)
{
int res=;
for(int i=;i<M;i++){
if((x>>i)&) res++;
}
if(res>=K) return true;
return false;
}
void solve()
{
dp[][][]=;
for(int i=;i<N;i++)
for(int j=;j<=cnt;j++)
for(int p=;p<(<<M);p++){
if(dp[i][j][p]){
for(int x=;x<;x++){
int newj=ch[j][x];
int newp=p|End[ch[j][x]];
dp[i+][newj][newp]+=dp[i][j][p];
dp[i+][newj][newp]%=Mod;
}
}
}
int ans=;
for(int i=;i<=cnt;i++)
for(int j=;j<(<<M);j++){
if(count(j)) ans=(ans+dp[N][i][j])%Mod;
}
cout<<ans<<endl;
}
}Trie;
int main()
{
while(~scanf("%d%d%d",&N,&M,&K)){
if(N==&&M==&&K==) return ;
Trie.update();
for(int i=;i<M;i++){
scanf("%s",s);
Trie.insert(i);
}
Trie.build();
Trie.solve();
}
return ;
}

HihoCder1640 : 命名的烦恼(AC自动机+状压DP)

题意:给定N个字符串,问最短的字符串长度,使之包含这N个字符串。
思路:用dis[u][k]表示走到u点,已经包含的字符串情况k。然后在单调优先队列在AC自动机上bfs找最短路。(由于u太大,当然直接bfs是超时了的,需要DP做)
//代码没有AC
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
const int inf=0x7fffffff;
int dp[][<<];
struct in
{
int dis,pos,opt;
in(int x,int y,int z):dis(x),pos(y),opt(z){}
friend bool operator < (in a,in b){
return a.dis>b.dis;
}
};
priority_queue<in>q;
int Next[maxn],ch[maxn][],End[maxn],N,cnt;
int que[maxn],head,tail; char s[maxn];
struct ACauto
{
void insert(int tag)
{
int Now=;
for(int i=;s[i];i++){
if(!ch[Now][s[i]-'a']) ch[Now][s[i]-'a']=++cnt;
Now=ch[Now][s[i]-'a'];
} End[Now]=<<tag;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i])
que[++head]=ch[][i];
}
while(tail<head){
int u=que[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
que[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
End[ch[u][i]]|=End[Next[ch[u][i]]];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void solve()
{
dp[][]=; q.push(in(,,));
while(!q.empty()){
in tmp=q.top(); q.pop();
int u=tmp.pos, k=tmp.opt;
for(int i=;i<;i++){
int v=ch[u][i],kk=k|End[v];
if(v==) continue;
if(kk==(<<N)-) {
printf("%d\n",dp[u][k]+);
return ;
}
if(dp[v][kk]>dp[u][k]+) {
dp[v][kk]=dp[u][k]+;
q.push(in(dp[v][kk],v,kk));
}
}
}
}
}Trie;
int main()
{
scanf("%d",&N);
for(int i=;i<N;i++){
scanf("%s",s);
Trie.insert(i);
}
Trie.build();
for(int i=;i<=cnt;i++)
for(int j=;j<(<<N);j++)
dp[i][j]=inf;
Trie.solve();
return ;
}

HDU2457:DNA repair(AC自动机+DP)

题意:给定一些病毒DNA,和一串人的NDA,问至少改变多少个核苷酸,使之不含病毒DNA。
思路:AC自动机把病毒NDA表示出来,在DP的时候不经过病毒DNA。 dp[len][X]表示前len个字符,最后一个字符在AC自动机上X点的最优解,方程易得。
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=;
const int Mod=;
const int inf=;
int dp[maxn][maxn];
int Next[maxn],ch[maxn][],End[maxn],N,cnt;
int que[maxn],head,tail; char s[maxn];
int id[],T;
struct ACauto
{
void update()
{
cnt=head=tail=;
memset(ch,,sizeof(ch));
memset(Next,,sizeof(Next));
memset(End,,sizeof(End));
}
void insert()
{
int Now=;
for(int i=;s[i];i++){
int x=id[s[i]];
if(!ch[Now][x]) ch[Now][x]=++cnt;
Now=ch[Now][x];
} End[Now]=;
}
void build()
{
for(int i=;i<;i++){
if(ch[][i])
que[++head]=ch[][i];
}
while(tail<head){
int u=que[++tail];
for(int i=;i<;i++){
if(ch[u][i]){
que[++head]=ch[u][i];
Next[ch[u][i]]=ch[Next[u]][i];
End[ch[u][i]]|=End[Next[ch[u][i]]];
}
else ch[u][i]=ch[Next[u]][i];
}
}
}
void solve()
{
scanf("%s",s);
int Len=strlen(s);
int ans=Len;
for(int i=;i<=Len;i++)
for(int j=;j<=cnt;j++) dp[i][j]=inf; dp[][]=; for(int i=;s[i];i++){
int x=id[s[i]];
// cout<<i<<" "<<s[i]<<" "<<x<<endl;
for(int j=;j<=cnt;j++){
if(dp[i][j]==inf) continue;
for(int p=;p<;p++){
if(End[ch[j][p]]) continue;
if(p==x) dp[i+][ch[j][p]]=min(dp[i+][ch[j][p]],dp[i][j]);
else dp[i+][ch[j][p]]=min(dp[i+][ch[j][p]],dp[i][j]+);
}
}
}
for(int i=;i<=cnt;i++) ans=min(ans,dp[Len][i]);
printf("Case %d: ",++T);
if(ans==Len) printf("-1\n");
else printf("%d\n",ans);
return ;
}
}Trie;
int main()
{
id['A']=; id['G']=; id['C']=; id['T']=;
while(~scanf("%d",&N)&&N!=){
Trie.update();
for(int i=;i<N;i++){
scanf("%s",s);
Trie.insert();
}
Trie.build();
Trie.solve();
}
return ;
}

【AC自动机&&Trie图】积累的更多相关文章

  1. 小菜鸟 菜谈 KMP->字典树->AC自动机->trie 图 (改进与不改进)

    本文的主要宗旨是总结自己看了大佬们对AC自动机和trie 图 的一些理解与看法.(前沿:本人水平有限,总结有误,希望大佬们可以指出) KMP分割线--------------------------- ...

  2. 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组

    涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...

  3. 洛谷P2444 [POI2000]病毒(AC自动机,DFS求环)

    洛谷题目传送门 AC自动机入门--yyb巨佬的博客 AC自动机入手经典好题(虽然年代久远) 有了fail指针,trie树就不是原来的树型结构了,我们可以把它叫做trie图,由父节点向子节点连的边和fa ...

  4. BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索

    题目描述 输入 输出 样例输入 10 2 hello world 样例输出 2 helloworld worldhello 提示 这题算是一个套路题了,多个串求都包含它们的长为L的串的方案数. 显然是 ...

  5. BZOJ 1030 [JSOI2007]文本生成器 (Trie图+DP)

    题目大意:给你一堆字符串,一个串不合法的条件是这些字符串中任意一个是这个串的子串,求合法的串的数量 其实这道题比 [HNOI2008]GT考试 那道题好写一些,但道理是一样的 只不过这道题的答案可以转 ...

  6. AC自动机讲解

    今天花了半天肝下AC自动机,总算啃下一块硬骨头,熬夜把博客赶出来.. 正如许多博客所说,AC自动机看似很难很妙,而事实上不难,但的确很妙.笼统地说,AC自动机=Trie+KMP,但是仅仅知道这个并没有 ...

  7. 「AC自动机」学习笔记

    AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...

  8. HDU 2296 Ring ( Trie图 && DP && DP状态记录)

    题意 : 给出 m 个单词,每一个单词有一个权重,如果一个字符串包含了这些单词,那么意味着这个字符串拥有了其权重,问你构成长度为 n 且权重最大的字符串是什么 ( 若有权重相同的,则输出最短且字典序最 ...

  9. 【模板】AC自动机(简单版)

    我:“woc...AC自动机?” 我:“可以自动AC???” 然鹅... 大佬:“傻...” 我:“(⊙_⊙)?” 大佬:“缺...” 我:“......” (大佬...卒 | 逃...) emm.. ...

随机推荐

  1. 顿悟:Linux是拿来用的,不是拿来折腾的

    Linux是拿来用的,而不是折腾其本身.相信这个道理不少聪明人(实用主义者)都明白,然而总是有那么一群人拿Linux去安装各种发行版.研究Linux命令.配置桌面.美化桌面.研究各种wm/DE.永无止 ...

  2. 深入探究Java中hashCode()和equals()的关系

    目录 一.基础:hashCode() 和 equals() 简介 equals() hashCode() 二. 漫谈:初识 hashCode() 与 equals() 之间的关系 三. 解密:深入理解 ...

  3. jmeter自定义并发用户数图形插件介绍

    Stepping Thread Group马上要被废弃了,废弃原因不知道,官方推荐使用 BlazeMeter Inc.公司贡献的插件Concurrency Thread Group,配合 Throug ...

  4. 微信小程序 - 音频播放(1.2版本和1.2版本之后)

    不多说了,直接贴code // 1.2版本以后便不在维护 wx.getBackgroundAudioManager({ success:function(res){ var status =res.s ...

  5. 小老虎CSDN博客流量分析

    小老虎CSDN博客流量分析 一.分析的博客对象 http://blog.csdn.net/littletigerat 二.分析的时间节点 2014年7月10日星期四 三.PV.UV以及IP值   wa ...

  6. insertion-sort-list——链表、插入排序、链表插入

    Sort a linked list using insertion sort. PS:需要考虑left为head且只有一个数时,此时left->==NULL,若right<left则应更 ...

  7. 简谈Java的join()方法(转)

    join()是Thread类的一个方法.根据jdk文档的定义: public final void join()throws InterruptedException: Waits for this ...

  8. Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor

    Webservice WCF WebApi   注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...

  9. 最简单的基于FFmpeg的AVDevice样例(读取摄像头)

    =====================================================最简单的基于FFmpeg的AVDevice样例文章列表: 最简单的基于FFmpeg的AVDev ...

  10. 第六十题(在O(1)时间内删除链表结点)

    题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点. 思路:将待删除节点下一个节点的数据赋给当前节点.删除下一个节点就可以.