目的:已知n个长度不同的模式串,以及一个长度为m的串S,求在S中出现过多少个模式串。
步骤:
①建立trie树
②建立失败指针
③字符串匹配
fail[now]:当前节点now的失败指针指向的地方
trie[now][i]:下一个字母为i+'a'的节点的下标为trie[now][i]
build过程:如果有这个子节点为i+'a',则让这个节点的失败指针指向他父亲节点的失败指针所指向的那个节点的下一个节点,否则就让当前节点的这个子节点指向当前节点fail指针的这个节点。
https://blog.csdn.net/bestsort/article/details/82947639
自用模板:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <bits/stdc++.h> using namespace std; const int MAX_N=10000; const int MAX_C=26; struct AC_Automaton { int ch[MAX_N][MAX_C],fail[MAX_N],cnt[MAX_N]; int tot; void init() { memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=0; memset(cnt,0,sizeof(cnt)); } void insert(char* str) { int p=0; for(int i=0;str[i];i++) { if(ch[p][str[i]-'a']==-1) { ch[p][str[i]-'a']=++tot; } p=ch[p][str[i]-'a']; } cnt[p]++; } void build() { int l=0,r=0,Q[MAX_N]; for(int i=0;i<MAX_C;i++) { if(ch[0][i]==-1) { ch[0][i]=0; } else { Q[r++]=ch[0][i]; } } while(l<r) { int p=Q[l++]; for(int i=0;i<MAX_C;i++) { if(ch[p][i]==-1) { ch[p][i]=ch[fail[p]][i]; } else { fail[ch[p][i]]=ch[fail[p]][i]; Q[r++]=ch[p][i]; } } } } int count(char* str) { int ret=0,p=0; for(int i=0;str[i];i++) { p=ch[p][str[i]-'a']; int tmp=p; while(tmp) { ret+=cnt[tmp]; cnt[tmp]=0; tmp=fail[tmp]; } } return ret; } }ac; int main() { ac.init(); ac.insert("abcd"); ac.insert("bcd"); ac.insert("cd"); ac.insert("d"); ac.build(); cout<<ac.count("abcd")<<endl; return 0; }
s中出现过的模式串都删除,删掉后可首尾连接重新包含模式串,模式串不会是另一个模式串的子串。请输出删除后的s
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int maxc=26; struct ac_automaton { int ch[maxn][maxc],fail[maxn],cnt[maxn],stk[maxn]; int tot,top; void init() { memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=top=0; memset(cnt,0,sizeof(cnt)); memset(stk,0,sizeof(stk)); } void insert(char* str) { int p=0; for(int i=0;str[i];i++) { if(ch[p][str[i]-'a']==-1)ch[p][str[i]-'a']=++tot; p=ch[p][str[i]-'a']; } cnt[p]=strlen(str); } void build() { int l=0,r=0,Q[maxn]; for(int i=0;i<maxc;i++) { if(ch[0][i]==-1)ch[0][i]=0; else Q[r++]=ch[0][i]; } while(l<r) { int p=Q[l++]; for(int i=0;i<maxc;i++) { if(ch[p][i]==-1)ch[p][i]=ch[fail[p]][i]; else fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i]; } } } void count(char* str) { int p=0,a=0; for(int i=0;str[i];i++) { p=ch[p][str[i]-'a']; str[a++]=str[i]; stk[top++]=p; if(cnt[p]) { a-=cnt[p]; top-=cnt[p]; p=top?stk[top-1]:0; } } str[a]='\0'; } }ac; int main() { char s[maxn],s2[maxn]; int n; cin>>s; cin>>n; ac.init(); while(n--) { cin>>s2; ac.insert(s2); } ac.build(); ac.count(s); cout<<s<<endl; return 0; }
ac自动机动态规划
求模式串出现次数
求模式串出现次数,如果包含模式串A、B,且B是A的子串,则只记录A:消除标记
求串中不包含任何模式串的串的种类数,串的长度为50,dp
长度为len的至少包含一个串的数量=所有情况-不包含任何串的串数量
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=1e3+5; const int maxc=26; int mod=10007; struct ac_automaton { int ch[maxn][maxc],fail[maxn],cnt[maxn],dp[maxn][maxn]; int tot; void init() { memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=0; memset(cnt,0,sizeof(cnt)); memset(dp,0,sizeof(dp)); } void insert(char* str) { int p=0; for(int i=0;str[i];i++) { if(ch[p][str[i]-'A']==-1)ch[p][str[i]-'A']=++tot; p=ch[p][str[i]-'A']; } cnt[p]++; } void build() { int l=0,r=0,Q[maxn]; for(int i=0;i<maxc;i++) { if(ch[0][i]==-1)ch[0][i]=0; else Q[r++]=ch[0][i]; } while(l<r) { int p=Q[l++]; for(int i=0;i<maxc;i++) { if(ch[p][i]==-1)ch[p][i]=ch[fail[p]][i]; else fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i],cnt[ch[p][i]]+=cnt[fail[ch[p][i]]]; } } } int count(int len) { int p=0,ans=0; dp[0][0]=1; for(int i=1;i<=len;i++) for(int j=0;j<=tot;j++) { for(int k=0;k<maxc;k++) { p=ch[j][k]; if(cnt[p]==0)dp[i][p]+=dp[i-1][j],dp[i][p]%=mod; } } for(int i=0;i<=tot;i++) { ans+=dp[len][i]; ans%=mod; } return ans; } }ac; int qpow(int a,int b,int p) { ll ans=1; while(b) { if(b&1)ans=ans*a%p; a=a*a%p; b>>=1; } return ans%p; } int main() { ac.init(); int n,len; cin>>n>>len; while(n--) { char ch[150]; cin>>ch; ac.insert(ch); } ac.build(); int ans=ac.count(len),tmp=qpow(26,len,mod); ans=(tmp-ans%mod+mod)%mod; cout<<ans<<endl; return 0; }
求串中不包含任何模式串的串的种类数,串的长度为2e9,矩阵乘法加速dp
求一个长度最短的串使得它包含所有模式串,状压dp
至少修改多少次才可以使得串中无出现过的模式串,只能修改成ABCD
dp[i][j]表示前i个字符状态为j时的最少修改次数,build时考虑是否存在失配指针仍然匹配,cnt[ch[p][i]]+=cnt[fail[ch[p][i]]]
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; #define inf 0x3f3f3f3f const int maxn=1e3+10; const int maxc=26; struct ac_automaton { int ch[maxn][maxc],fail[maxn],cnt[maxn],dp[maxn][maxn]; int tot; void init() { memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=0; memset(cnt,0,sizeof(cnt)); memset(dp,inf,sizeof(dp)); } void insert(char* str) { int p=0; for(int i=0;str[i];i++) { if(ch[p][str[i]-'A']==-1)ch[p][str[i]-'A']=++tot; p=ch[p][str[i]-'A']; } cnt[p]++; } void build() { int l=0,r=0,Q[maxn]; for(int i=0;i<maxc;i++) { if(ch[0][i]==-1)ch[0][i]=0; else Q[r++]=ch[0][i]; } while(l<r) { int p=Q[l++]; for(int i=0;i<maxc;i++) { if(ch[p][i]==-1)ch[p][i]=ch[fail[p]][i]; else fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i],cnt[ch[p][i]]+=cnt[fail[ch[p][i]]]; } } } int count(char* str) { int p; dp[0][0]=0; for(int i=1;i<=strlen(str);i++) { for(int j=0;j<tot;j++) { for(int k=0;k<maxc;k++) { p=ch[j][k]; if(cnt[p])continue; if(str[i-1]-'A'==k)dp[i][p]=min(dp[i][p],dp[i-1][j]); else dp[i][p]=min(dp[i][p],dp[i-1][j]+1); } } } int len=strlen(str),maxn=inf; for(int i=0;i<tot;i++) { if(cnt[i]==0) maxn=min(maxn,dp[len][i]); } return maxn; } }ac; int main() { ac.init(); int n; scanf("%d",&n); while(n--) { char ch[25]; scanf("%s",ch); ac.insert(ch); } ac.build(); char ss[maxn]; scanf("%s",ss); int ans=ac.count(ss); if(ans==inf)printf("-1\n"); else printf("%d\n",ans); return 0; }
ac自动机+状压dp
https://www.cnblogs.com/songorz/p/11459289.html
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> using namespace std; const int inf=0x3f3f3f3f; const int maxn=1010; int n,m,w[20]; int getans(int x) { int ans=0; for(int i=0;i<n;i++) { if(x&(1<<i)) ans+=w[i]; } return ans; } struct ac_automaton { int ch[maxn][4],fail[maxn],val[maxn],dp[2][maxn][1<<11],ans[maxn]; int tot; void init() { memset(ch,-1,sizeof(ch)); memset(fail,0,sizeof(fail)); tot=0; memset(val,0,sizeof(val)); } void insert(char* str,int x) { int p=0; for(int i=0;str[i];i++) { if(ch[p][str[i]-'a']==-1) { ch[p][str[i]-'a']=++tot; } p=ch[p][str[i]-'a']; } val[p]=1<<x; } void build() { int l=0,r=0,Q[maxn]; for(int i=0;i<4;i++) { if(ch[0][i]==-1)ch[0][i]=0; else Q[r++]=ch[0][i]; } while(l<r) { int p=Q[l++]; for(int i=0;i<4;i++) { if(ch[p][i]==-1)ch[p][i]=ch[fail[p]][i]; else fail[ch[p][i]]=ch[fail[p]][i],Q[r++]=ch[p][i],val[p]|=val[fail[p]]; } } } void work() { memset(dp,0,sizeof(dp)); dp[0][0][0]=1; for(int i=1;i<=m;i++) { memset(dp[i&1],0,sizeof dp[i&1]); for(int j=0;j<=tot;j++) { for(int k=0;k<4;k++) { for(int z=0;z<(1<<n);z++) { if(dp[(i-1)&1][j][z]) dp[i&1][ch[j][k]][z|val[ch[j][k]]]=1; } } } } } int count() { int res=-inf; for(int i=0;i<(1<<n);i++) { ans[i]=getans(i); } for(int i=0;i<=tot;i++) { for(int j=0;j<(1<<n);j++) { if(dp[m&1][i][j]) res=max(res,ans[j]); } } return res; } }ac; int main() { ac.init(); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { char str[105]; scanf("%s%d",str,&w[i]); ac.insert(str,i); } ac.build(); ac.work(); int res=ac.count(); if(res<0)puts("Unhappy!"); else printf("%d\n",res); return 0; }
ac自动机+树状数组
https://www.cnblogs.com/songorz/p/11441135.html
...