【AC自动机&&Trie图】积累
以前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图】积累的更多相关文章
- 小菜鸟 菜谈 KMP->字典树->AC自动机->trie 图 (改进与不改进)
本文的主要宗旨是总结自己看了大佬们对AC自动机和trie 图 的一些理解与看法.(前沿:本人水平有限,总结有误,希望大佬们可以指出) KMP分割线--------------------------- ...
- 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...
- 洛谷P2444 [POI2000]病毒(AC自动机,DFS求环)
洛谷题目传送门 AC自动机入门--yyb巨佬的博客 AC自动机入手经典好题(虽然年代久远) 有了fail指针,trie树就不是原来的树型结构了,我们可以把它叫做trie图,由父节点向子节点连的边和fa ...
- BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索
题目描述 输入 输出 样例输入 10 2 hello world 样例输出 2 helloworld worldhello 提示 这题算是一个套路题了,多个串求都包含它们的长为L的串的方案数. 显然是 ...
- BZOJ 1030 [JSOI2007]文本生成器 (Trie图+DP)
题目大意:给你一堆字符串,一个串不合法的条件是这些字符串中任意一个是这个串的子串,求合法的串的数量 其实这道题比 [HNOI2008]GT考试 那道题好写一些,但道理是一样的 只不过这道题的答案可以转 ...
- AC自动机讲解
今天花了半天肝下AC自动机,总算啃下一块硬骨头,熬夜把博客赶出来.. 正如许多博客所说,AC自动机看似很难很妙,而事实上不难,但的确很妙.笼统地说,AC自动机=Trie+KMP,但是仅仅知道这个并没有 ...
- 「AC自动机」学习笔记
AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...
- HDU 2296 Ring ( Trie图 && DP && DP状态记录)
题意 : 给出 m 个单词,每一个单词有一个权重,如果一个字符串包含了这些单词,那么意味着这个字符串拥有了其权重,问你构成长度为 n 且权重最大的字符串是什么 ( 若有权重相同的,则输出最短且字典序最 ...
- 【模板】AC自动机(简单版)
我:“woc...AC自动机?” 我:“可以自动AC???” 然鹅... 大佬:“傻...” 我:“(⊙_⊙)?” 大佬:“缺...” 我:“......” (大佬...卒 | 逃...) emm.. ...
随机推荐
- 湘潭oj1203/邀请赛A题 数论+java大数
求 n%1+n%2+n%3+n%4+.........n%n=,n<=10^12次. 开始时盲目地找规律,结果一无所获.后来经学长点拨,天资愚钝,搞了半天才明白. 先上图: 对于该题,在求区间( ...
- eclipse 安卓虚拟机安装apk 及常见问题
首先必须启动虚拟机然后如图操作:
- hdu - 1269 迷宫城堡 (强连通裸题)
http://acm.hdu.edu.cn/showproblem.php?pid=1269 判断一个图是不是强连通,缩点之后判断顶点数是不是为1即可. #include <iostream&g ...
- MVC模式(三层架构模式)
(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller). MVC模式最早由Try ...
- ubuntu远程桌面设置
一.服务器端电脑设置: 1.在搜索端搜索desktop sharing,然后设置后退出 二.客户端电脑设置: 1.在搜索端搜索remmina remote desktop client 2.如图设置: ...
- 因chmod /usr致使raspberryPi重装
一.系统安装noobs 设置用户名及密码,设置超级用户root密码: sudo passwd root,回车后按提示输入两次root的密码(注意,输入时是不会提示*号的,直接输入即可) 二.源及软件 ...
- stm32f103c8t6命名
stm32f103c8t6和stm32f103rbt c8:48脚.64k :rb:64脚.128k.
- Codeforces Gym 100286I iSharp 模拟
原题地址:http://codeforces.com/gym/100286/attachments/download/2013/20082009-acmicpc-northeastern-europe ...
- NOIPSB评测机+SB题DAY2
忍者钩爪 题目描述 小 Q 是一名酷爱钩爪的忍者, 最喜欢飞檐走壁的感觉, 有一天小 Q 发现一个练习使用钩 爪的好地方,决定在这里大显身手. 场景的天花板可以被描述为一个无穷长的数轴, 初始小 Q ...
- 关于linter
各类代码都有规则格式检查工具,称之为linter 比如:csslint/jslint/eslint/pylint sumlime提供了一个linter的框架SublimeLinter,在里面可以使用各 ...