https://blog.csdn.net/lingzidong/article/details/80714431
AC自动机两大题型:查询字串、建立有向图DP(常数较大串数较少需要矩阵快速幂)
AC自动机学习:18.10.1
给定n个单词和1个句子,求句子中不同单词的个数
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+5;
struct Trie
{
static const int MAXN=26;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=buf[i]-'a';
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
++e[now];
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
}
}
}
int query(char *buf)
{
int len=strlen(buf),now=rt,res=0;
for(int i=0;i<len;++i)
{
now=nxt[now][buf[i]-'a'];
int tmp=now;
while(tmp!=rt)
{
res+=e[tmp];
e[tmp]=0;
tmp=f[tmp];
}
}
return res;
}
};
char s[MAX];
int n,t;
Trie AC;
int main()
{
scanf("%d",&t);
while(t--)
{
AC.init();
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%s",s);
AC.insert(s);
}
AC.build();
scanf("%s",s);
printf("%d\n",AC.query(s));
}
return 0;
}
给定N个病毒串M个匹配串,对于每个匹配串查询其子串
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
struct Trie
{
static const int MAXN=128;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf,int id)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=buf[i];
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
e[now]=id;
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
}
}
}
void query(char *buf,set<int>&stt)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
now=nxt[now][buf[i]];
int tmp=now;
while(tmp!=rt)
{
if(e[tmp]!=0) stt.insert(e[tmp]);
tmp=f[tmp];
}
}
}
};
char s[MAX];
int n,m;
Trie AC;
set<int>st;
set<int>::iterator p;
int main()
{
AC.init();
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%s",s);
AC.insert(s,i);
}
AC.build();
scanf("%d",&m);
int ans=0;
for(int i=1;i<=m;++i)
{
scanf("%s",s);
st.clear();
AC.query(s,st);
if(!st.empty())
{
printf("web %d:",i);
for(p=st.begin();p!=st.end();++p) printf(" %d",*p);
printf("\n");++ans;
}
}
printf("total: %d\n",ans);
return 0;
}
给定n个病毒序列,求长度为m的健康序列的种数
AC自动机跑有向图邻接矩阵,求根节点可到达的非病毒终点的位置,矩阵的k次为走k步可到达的位置
e记录病毒终点,以及f指针指向病毒终点的点(该串的子串是病毒串,例如健康串abc,病毒串bc,b->b,c->c,其中c是病毒终点)
答案为根节点所在的行值相加
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=105;
const int MOD=1e5;
int L=0;
inline int id(char c)
{
if(c=='A') return 0;
if(c=='C') return 1;
if(c=='T') return 2;
return 3;
}
struct Mat
{
long long m[MAX][MAX];
Mat(){memset(m,0,sizeof(m));}
};
Mat multi(const Mat &a,const Mat &b)
{
Mat c;
for(int i=0;i<L;i++)
for(int j=0;j<L;j++)if(a.m[j][i]!=0)
for(int k=0;k<L;k++)if(b.m[i][k]!=0)
c.m[j][k]=(c.m[j][k]+a.m[j][i]*b.m[i][k]%MOD)%MOD;
return c;
}
Mat pow(Mat &a,int k)
{
Mat b;
for(int i=0;i<L;i++) b.m[i][i]=1;
while(k)
{
if(k&1) b=multi(b,a);
a=multi(a,a);
k>>=1;
}
return b;
}
int q[MAX],l,r;
struct Trie
{
static const int MAXN=4;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=id(buf[i]);
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
e[now]=1;
}
void build()
{
l=0,r=-1;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q[++r]=nxt[rt][i];
}
while(l<=r)
{
int now=q[l++];
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q[++r]=nxt[now][i];
e[nxt[now][i]]|=e[f[nxt[now][i]]];
}
}
}
void get(Mat &a)
{
for(int i=0;i<L;++i)if(!e[i])
for(int j=0;j<MAXN;++j)if(!e[nxt[i][j]])
a.m[i][nxt[i][j]]=(a.m[i][nxt[i][j]]+1)%MOD;
}
};
char s[MAX];
int n,m;
Trie AC;
int main()
{
while(~scanf("%d%d",&n,&m))
{
AC.init();
for(int i=0;i<n;++i)
{
scanf("%s",s);
AC.insert(s);
}
AC.build();
Mat a;AC.get(a);
a=pow(a,m);
long long sum=0;
for(int i=0;i<L;++i) sum=(sum+a.m[0][i])%MOD;
printf("%lld\n",sum);
}
return 0;
}
给定m个子串,求组成长度为n的由K个以上子串组成的匹配串的方案数
dp[i][j][k],i记录串的长度,j记录到达的节点,k记录目前走过的完整串的状态
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=105;
const int MOD=20090717;
struct Trie
{
static const int MAXN=26;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf,int id)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=buf[i]-'a';
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
e[now]|=1<<id;
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
e[nxt[now][i]]|=e[f[nxt[now][i]]];
}
}
}
}AC;
char s[MAX];
int n,m,K,cnt[1<<10];
long long dp[26][MAX][1<<10];
int main()
{
memset(cnt,0,sizeof(cnt));
for(int i=1;i<(1<<10);++i)
for(int j=0;j<10;++j)if(i&(1<<j))
++cnt[i];
while(~scanf("%d%d%d",&n,&m,&K))
{
if(n==0&&m==0&&K==0) break;
AC.init();
for(int i=0;i<m;++i)
{
scanf("%s",s);
AC.insert(s,i);
}
AC.build();
for(int i=0;i<=n;++i)
for(int j=0;j<AC.L;++j)
for(int k=0;k<(1<<m);++k)
dp[i][j][k]=0;
dp[0][0][0]=1;
for(int i=0;i<n;++i)
for(int j=0;j<AC.L;++j)
for(int k=0;k<(1<<m);++k) if(dp[i][j][k]>0)
for(int p=0;p<AC.MAXN;++p)
{
int xi=i+1,xj=AC.nxt[j][p],xk=k|AC.e[xj];
dp[xi][xj][xk]=(dp[xi][xj][xk]+dp[i][j][k])%MOD;
}
long long ans=0;
for(int k=0;k<(1<<m);++k)if(cnt[k]>=K)
for(int j=0;j<AC.L;++j)
ans=(ans+dp[n][j][k])%MOD;
printf("%lld\n",ans);
}
return 0;
}
给定子串及其价值,求由这些串组成的长度为n价值最大字典序最小的串
dp[i][j],i表示串的长度,j表示到达的节点
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=1e3+1e2+5;
const int INF=0x3f3f3f3f;
struct Trie
{
static const int MAXN=26;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf,int id)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=buf[i]-'a';
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
e[now]=id;
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
}
}
}
}AC;
bool cmp(char *a,char *b)
{
int al=strlen(a),bl=strlen(b);
return al==bl?strcmp(a,b)<0:al<bl;
}
char s[15],str[55][MAX][55],tmp[55],ans[55];
int n,m,t,val[105],dp[55][MAX];
int main()
{
scanf("%d",&t);
while(t--)
{
AC.init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%s",s);
AC.insert(s,i);
}
for(int i=1;i<=m;++i) scanf("%d",&val[i]);
AC.build();
for(int i=0;i<=n;++i)
for(int j=0;j<AC.L;++j)
dp[i][j]=-INF;
dp[0][0]=0;
strcpy(str[0][0],"");strcpy(ans,"");
int max_=0;
for(int i=0;i<n;++i)
for(int j=0;j<AC.L;++j)if(dp[i][j]>=0)
{
strcpy(tmp,str[i][j]);
int len=strlen(tmp);
for(int k=0;k<AC.MAXN;++k)
{
int xi=i+1,xj=AC.nxt[j][k],t=dp[i][j];
tmp[len]='a'+k;
tmp[len+1]='\0';
if(AC.e[xj]) t+=val[AC.e[xj]];
if(dp[xi][xj]<t||(dp[xi][xj]==t&&cmp(tmp,str[xi][xj])))
{
dp[xi][xj]=t;
strcpy(str[xi][xj],tmp);
if(max_<t||(max_==t&&cmp(tmp,ans)))
{
max_=t;
strcpy(ans,tmp);
}
}
}
}
printf("%s\n",ans);
}
return 0;
}
给定n个子串,求一个串经过重排所能包含的最多字串的个数
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=505;
const int INF=0x3f3f3f3f;
int id(char c)
{
if(c=='A') return 0;
if(c=='C') return 1;
if(c=='G') return 2;
else return 3;
}
struct Trie
{
static const int MAXN=4;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=id(buf[i]);
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
++e[now];
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
e[nxt[now][i]]+=e[f[nxt[now][i]]];
}
}
}
}AC;
char s[1005];
int n,cnt[4],bit[4],dp[MAX][20005];
int main()
{
int tc=1;
while(~scanf("%d",&n))
{
if(n==0) break;
AC.init();
for(int i=0;i<n;++i)
{
scanf("%s",s);
AC.insert(s);
}
AC.build();
scanf("%s",s);
int len=strlen(s);
memset(cnt,0,sizeof(cnt));
memset(bit,0,sizeof(bit));
memset(dp,-INF,sizeof(dp));
for(int i=0;i<len;++i) ++cnt[id(s[i])];
bit[3]=1;bit[2]=cnt[3]+1;bit[1]=bit[2]*(cnt[2]+1);bit[0]=bit[1]*(cnt[1]+1);
dp[0][0]=0;
for(int a=0;a<=cnt[0];++a)
for(int c=0;c<=cnt[1];++c)
for(int g=0;g<=cnt[2];++g)
for(int t=0;t<=cnt[3];++t)
{
int ha=a*bit[0]+c*bit[1]+g*bit[2]+t*bit[3];
for(int i=0;i<AC.L;++i)if(dp[i][ha]>=0)
for(int j=0;j<AC.MAXN;++j)
{
if(j==0&&a==cnt[0]) continue;
if(j==1&&c==cnt[1]) continue;
if(j==2&&g==cnt[2]) continue;
if(j==3&&t==cnt[3]) continue;
int xi=AC.nxt[i][j],xha=ha+bit[j];
dp[xi][xha]=max(dp[xi][xha],dp[i][ha]+AC.e[xi]);
}
}
int ha=cnt[0]*bit[0]+cnt[1]*bit[1]+cnt[2]*bit[2]+cnt[3]*bit[3];
int ans=0;
for(int i=0;i<AC.L;++i) ans=max(ans,dp[i][ha]);
printf("Case %d: %d\n",tc++,ans);
}
return 0;
}
HDU3247 Resource Archiver
给定n个好串,m个坏串,求最短的走遍好串不走坏串的最短串的长度
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int MAX=2e6;
const int INF=0x3f3f3f3f;
int dis[MAX],pos[11],g[11][11],cnt;
struct Trie
{
static const int MAXN=2;
int nxt[MAX][MAXN],f[MAX],e[MAX];
int rt,L;
int newnode()
{
for(int i=0;i<MAXN;++i) nxt[L][i]=-1;
e[L++]=0;
return L-1;
}
void init(){L=0;rt=newnode();}
void insert(char *buf,int v)
{
int len=strlen(buf),now=rt;
for(int i=0;i<len;++i)
{
int x=buf[i]-'0';
if(nxt[now][x]==-1) nxt[now][x]=newnode();
now=nxt[now][x];
}
e[now]=v;
}
void build()
{
queue<int>q;
f[rt]=rt;
for(int i=0;i<MAXN;++i)
if(nxt[rt][i]==-1) nxt[rt][i]=rt;
else
{
f[nxt[rt][i]]=rt;
q.push(nxt[rt][i]);
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<MAXN;++i)
if(nxt[now][i]==-1) nxt[now][i]=nxt[f[now]][i];
else
{
f[nxt[now][i]]=nxt[f[now]][i];
q.push(nxt[now][i]);
if(e[f[nxt[now][i]]]==-1) e[nxt[now][i]]=-1;
}
}
}
void spfa(int k)
{
queue<int>q;
memset(dis,-1,sizeof(dis));
dis[pos[k]]=0;
q.push(pos[k]);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<2;++i)
{
int tmp=nxt[now][i];
if(dis[tmp]<0&&e[tmp]>=0)
{
dis[tmp]=dis[now]+1;
q.push(tmp);
}
}
}
for(int i=0;i<cnt;++i) g[k][i]=dis[pos[i]];
}
}AC;
char s[1005];
int n,m,dp[1005][11];
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
AC.init();
for(int i=0;i<n;++i) {scanf("%s",s);AC.insert(s,1<<i);}
for(int i=0;i<m;++i) {scanf("%s",s);AC.insert(s,-1);}
AC.build();
pos[0]=0;cnt=1;
for(int i=0;i<AC.L;++i)if(AC.e[i]>0) pos[cnt++]=i;
for(int i=0;i<cnt;++i) AC.spfa(i);
for(int i=0;i<(1<<n);++i)
for(int j=0;j<cnt;++j)
dp[i][j]=INF;
dp[0][0]=0;
for(int i=0;i<(1<<n);++i)
for(int j=0;j<cnt;++j)if(dp[i][j]<INF)
for(int k=0;k<cnt;++k)if(g[j][k]>=0&&j!=k)
dp[i|AC.e[pos[k]]][k]=min(dp[i|AC.e[pos[k]]][k],dp[i][j]+g[j][k]);
int ans=INF;
for(int i=0;i<cnt;++i) ans=min(ans,dp[(1<<n)-1][i]);
printf("%d\n",ans);
}
return 0;
}