ac自动机及相关dp
来源:互联网 发布:讯佳摇杆淘宝 编辑:程序博客网 时间:2024/06/05 22:44
终于会写ac自动机了。。。记得高一时充满了对这个算法的恐慌。。。
ac自动机的实际与kmp十分相像,不过它是有一堆匹配字符串。具体原理请见其他的博客。
首先,在szc大佬(%%%orz)的模板演示下,以及hzwer的模板参考下,终于自己独立写出了自己的模板(指针版真的遭不住)。。。(请神犇们orz不要嘲笑我这个蒟蒻。。)//在下的模板以hdu2222为准
然后贴两道例题
hdu2222(最裸的初学者必做题)
#include<iostream>#include<math.h>#include<algorithm>#include<string.h>#include<stdio.h>using namespace std;const int N=1000050;int cnt,c[N][26],fail[N],value[N];bool vis[N];void init(){ cnt=1; memset(fail,0,sizeof(fail)); memset(value,0,sizeof(value)); memset(vis,0,sizeof(vis)); memset(c,0,sizeof(c)); for(int i=0;i<=25;i++) c[0][i]=1; fail[1]=0;}void ins(char *str){ int len=strlen(str); int now=1; for(int i=0;i<len;i++) { int index=str[i]-'a'; if(!c[now][index]) c[now][index]=++cnt; now=c[now][index]; } value[now]++;}int q[N],head,tail;void buildac(){ head=tail=0; q[++tail]=1; while(head!=tail) { int now=q[++head]; for(int i=0;i<=25;i++) { if(c[now][i]) { int k=fail[now]; while(c[k][i]==0) k=fail[k]; fail[c[now][i]]=c[k][i]; q[++tail]=c[now][i]; } } }}int solve(char *aim){ int len=strlen(aim); int now=1; int index; int result=0; for(int i=0;i<len;i++) { index=aim[i]-'a'; while(!c[now][index]) now=fail[now]; now=c[now][index]; int temp=now; while(temp!=1&&!vis[temp]) { result+=value[temp]; vis[temp]=1; temp=fail[temp]; } } return result;}int T,n;char cc[N];int main(){ scanf("%d",&T); while(T--) { init(); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%s",cc),ins(cc); buildac(); scanf("%s",cc); printf("%d\n",solve(cc)); }}
bzoj2754
由于可延伸点数过多,所以用map来存储。
#include<iostream>#include<math.h>#include<algorithm>#include<string.h>#include<stdio.h>#include<map>#include<vector>using namespace std;const int N=100050;const int M=20050;vector<int> name[M],st[N];map<int ,int > c[N];bool vis[N],mark[N];int value[N],fail[N];int cnt;void init(){cnt=1;for(int i=-1;i<=10000;i++)c[0][i]=1;}void ins(int id){int len;scanf("%d",&len);int now=1,x;for(int i=0;i<len;i++){scanf("%d",&x);if(!c[now][x])c[now][x]=++cnt;now=c[now][x];}st[now].push_back(id);}int q[N];void buildac(){int head=0,tail=0;int now;q[++tail]=1;while(head!=tail){now=q[++head];for(map<int,int>::iterator i=c[now].begin();i!=c[now].end();i++ ){int t=i->first;int k=fail[now];while(!c[k][t]) k=fail[k];fail[i->second]=c[k][t];q[++tail]=i->second;}}}vector<int> tongji1,tongji2;int n,m,L,x,ans1[N],ans2[N];void get(int now,int id){for(int i=now;i!=1;i=fail[i]){if(!vis[i]){vis[i]=1,tongji1.push_back(i);for(int j=0;j<st[i].size();j++){if(!mark[st[i][j]]){ans1[id]++;ans2[st[i][j]]++;mark[st[i][j]]=1;tongji2.push_back(st[i][j]);}}}elsebreak;}}void solve(int g){int now=1;int len=name[g].size();for(int i=0;i<len;i++){while(!c[now][name[g][i]])now=fail[now];now=c[now][name[g][i]],get(now,g);}for(int i=0;i<tongji1.size();i++)vis[tongji1[i]]=0;for(int i=0;i<tongji2.size();i++)mark[tongji2[i]]=0;tongji1.clear();tongji2.clear();}int main(){init();scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&L);for(int j=1;j<=L;j++)scanf("%d",&x),name[i].push_back(x);name[i].push_back(-1);scanf("%d",&L);for(int j=1;j<=L;j++)scanf("%d",&x),name[i].push_back(x);}for(int i=1;i<=m;i++)ins(i);buildac();for(int i=1;i<=n;i++)solve(i);for(int i=1;i<=m;i++)printf("%d\n",ans2[i]);for(int i=1;i<=n;i++){printf("%d",ans1[i]);if(i!=n)printf(" ");}}
之后便是ac自动机+dp了
一般是先将trie树建造并构造fail,然后在这颗树上做匹配问题。
主要类型(在下目前所见,会更新):
1.不能出现哪些串
在这些串的结尾表上一个danger,如果一个节点的fail有danger,那么它也有(因为当前结点的后缀是fail指向的串,所以说明到当前结点时,fail节点所代表的串在这里已经被包含了,所以若其不能走,此也不能走)。
dp[i][j]表示到节点j已经选取了长度为i的合法串的方法数,每次若子节点是danger的,就不转移,若这个字母没有在这个节点的儿子,便转移到root上就行了。
poj3691
#include<iostream>#include<math.h>#include<algorithm>#include<stdio.h>#include<string.h>using namespace std;const int N=6050;const int INF=0x3f3f3f3f;int c[N][5];int value[N],cnt,fail[N];int dp[1005][N];bool vis[N];void init(){memset(dp,0,sizeof(dp));memset(value,0,sizeof(value));memset(c,0,sizeof(c));memset(fail,0,sizeof(fail));for(int i=0;i<=3;i++)c[0][i]=1;cnt=1;}int getd(char s){if(s=='A')return 0;if(s=='C')return 1;if(s=='T')return 2;if(s=='G')return 3;}void ins(char *str){int len=strlen(str);int now=1;for(int i=0;i<len;i++){int index=getd(str[i]);if(!c[now][index])c[now][index]=++cnt;now=c[now][index];}value[now]=1;}int q[N];void buildac(){int now,head=0,tail=0;q[++tail]=1;while(head!=tail){now=q[++head];for(int i=0;i<=3;i++){if(c[now][i]){int k=fail[now];while(!c[k][i])k=fail[k];fail[c[now][i]]=c[k][i];q[++tail]=c[now][i];value[c[now][i]]|=value[c[k][i]];}}}}char st[1005];int n;int solve(){int now=1;int len=strlen(st);for(int i=0;i<=len;i++)for(int j=1;j<=cnt;j++)dp[i][j]=INF;dp[0][1]=0;for(int i=1;i<=len;i++){for(int j=1;j<=cnt;j++){if(dp[i-1][j]!=INF){for(int k=0;k<=3;k++){if(c[j][k]){if(!value[c[j][k]])dp[i][c[j][k]]=min(dp[i][c[j][k]],dp[i-1][j]+(k!=getd(st[i-1])));}else {int f=fail[j];while(!c[f][k]) f=fail[f];if(!value[c[f][k]])dp[i][c[f][k]]=min(dp[i][c[f][k]],dp[i-1][j]+(k!=getd(st[i-1])));}}}}}int minans=INF;for(int i=1;i<=cnt;i++)minans=min(minans,dp[len][i]);return minans==INF?-1:minans;}int test=0;int main(){while(scanf("%d",&n)==1){if(n==0)break;init();for(int i=1;i<=n;i++)scanf("%s",st),ins(st);buildac();scanf("%s",st);printf("Case %d: %d\n",++test,solve());}}
2.要出现哪些串的方法数
这时候要使用状压dp来做,每一个节点维护一个value表示走到了这个节点能匹配哪些串,一个节点value要或 fail所指的value,原理同上。
dp[i][j][state]表示到j,选取了长度为i的串,state表示已经匹配了哪些串的方法数。如果不问方案数而问最大价值,value就维护成+=value[fail[now]]即可,毕竟是一个道理。
注意,无论是value还是danger应该在buildfail时维护,因为在维护fail时是bfs,所以当计算一个节点的value时,它的fail所指节点的value一定是完完全全的解决了(因为fail所指一定深度小于自己,毕竟bfs嘛),所以只需要关心fail的value即可。
bzoj1030
#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>using namespace std;const int N=6050;const int mo=10007;int c[N][26],cnt;int fail[N];int dp[105][N];int value[N];void init(){for(int i=0;i<=25;i++)c[0][i]=1;cnt=1;dp[0][1]=1;}void ins(char *str){int len=strlen(str);int now=1;for(int i=0;i<len;i++){int index=str[i]-'A';if(!c[now][index])c[now][index]=++cnt;now=c[now][index];}value[now]=1;}int q[N];void buildac(){int now,head=0,tail=0;q[++tail]=1;while(head!=tail){now=q[++head];for(int i=0;i<=25;i++){if(c[now][i]){int k=fail[now];while(!c[k][i])k=fail[k];fail[c[now][i]]=c[k][i];value[c[now][i]]|=value[c[k][i]];q[++tail]=c[now][i];}}}}int n,m;char st[N];void dpx(){for(int x=1;x<=m;x++){for(int i=1;i<=cnt;i++){if(!dp[x-1][i]||value[i])continue;for(int j=0;j<=25;j++){int k=i;while(!c[k][j])k=fail[k];dp[x][c[k][j]]=(dp[x][c[k][j]]+dp[x-1][i])%mo;}}}}int ans1,ans2;int lpow(int a,int b){int ans=1;while(b){if(b&1)ans=(ans*a)%mo;a=(a*a)%mo;b>>=1;}return ans;}int main(){init();scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%s",st),ins(st);buildac();dpx();int ans1=lpow(26,m);for(int i=1;i<=cnt;i++)if(!value[i]) ans2=(ans2+dp[m][i])%mo;printf("%d\n",(ans1-ans2+mo)%mo);}
//当然也可以像这样容斥
hdu2825
include<iostream>#include<string.h>#include<algorithm>#include<math.h>#include<stdio.h>using namespace std;const int N=1010;const int mo=20090717;int c[N][26],cnt,fail[N],value[N];long long dp[26][1100][105],ans;void init(){ for(int i=1;i<=1001;i++) for(int j=0;j<=25;j++) c[i][j]=0; for(int i=1;i<=1001;i++) value[i]=0,fail[i]=0; for(int i=0;i<=25;i++) c[0][i]=1; cnt=1; ans=0;}void ins(char *str,int id){ int len=strlen(str); int now=1; for(int i=0;i<len;i++) { int index=str[i]-'a'; if(!c[now][index]) c[now][index]=++cnt; now=c[now][index]; } value[now]|=(1<<(id-1));}int q[N];void buildac(){ int head=0,k,tail=0,now; q[++tail]=1; while(head!=tail) { int now=q[++head]; for(int i=0;i<=25;i++) { if(c[now][i]) { k=fail[now]; while(!c[k][i]) k=fail[k]; fail[c[now][i]]=c[k][i]; q[++tail]=c[now][i]; value[c[now][i]]|=value[fail[c[now][i]]]; } } }}int n,m,f,state;int tongji(int x){ int as=0; while(x) { as+=(x&1); x>>=1; } return as;}void solve(){ for(int i=0;i<=n;i++) for(int j=0;j<=state;j++) for(int k=1;k<=cnt;k++) dp[i][j][k]=0LL; dp[0][0][1]=1LL; int t; for(int i=1;i<=n;i++) { for(int j=0;j<=state;j++) { for(int k=1;k<=cnt;k++) { if(!dp[i-1][j][k]) continue; for(int x=0;x<=25;x++) { t=k; while(!c[t][x]) t=fail[t]; dp[i][j|value[c[t][x]]][c[t][x]]=(dp[i][j|value[c[t][x]]][c[t][x]]+dp[i-1][j][k])%mo; } } } } for(int i=0;i<=state;i++) { if(tongji(i)<f) continue; for(int j=1;j<=cnt;j++) ans=(ans+dp[n][i][j])%mo; }}char st[15];int main(){ while(scanf("%d%d%d",&n,&m,&f)==3) { if(n==m&&m==f&&f==0) break; init(); state=(1<<m)-1; for(int i=1;i<=m;i++) scanf("%s",st),ins(st,i); buildac(); solve(); printf("%lld\n",ans); }}
3.其他类型的dp
例如
bzoj2938
#include<iostream>#include<math.h>#include<string.h>#include<stdio.h>#include<algorithm>using namespace std;const int N=30050;int c[N][2],fail[N],cnt,value[N];bool vis[N];void init(){c[0][1]=c[0][0]=1;cnt=1;}void ins(char *a){int now=1;int len=strlen(a);for(int i=0;i<len;i++){int index=a[i]-'0';if(!c[now][index])c[now][index]=++cnt;now=c[now][index];}value[now]=1;}int q[N];void buildac(){int now;int head=0,tail=0;q[++tail]=1;while(head!=tail){int now=q[++head];for(int i=0;i<=1;i++){if(c[now][i]){int k=fail[now];while(!c[k][i])k=fail[k];fail[c[now][i]]=c[k][i];value[c[now][i]]|=value[c[k][i]];q[++tail]=c[now][i];}elsec[now][i]=c[fail[now]][i];}}}bool in[N];bool dfs(int x){in[x]=1;for(int i=0;i<=1;i++){int v=c[x][i];if(in[v])return 1;if(vis[v]||value[v])continue;vis[v]=1;if(dfs(v)) return 1;}in[x]=0;return 0;}char st[N];int n;int main(){scanf("%d",&n);init();for(int i=1;i<=n;i++)scanf("%s",st),ins(st);buildac();if(dfs(1))printf("TAK\n");else printf("NIE\n");}
4.甚至可能只是简单的递推
bzoj3172
#include<iostream>#include<string.h>#include<stdio.h>#include<algorithm>#include<math.h>using namespace std;const int N=1000050;int c[N][26],n,fail[N],cnt;int loc[N],value[N];void init(){for(int i=0;i<=25;i++)c[0][i]=1;cnt=1;}void ins(char *str,int id){int len=strlen(str);int now=1;for(int i=0;i<len;i++){int index=str[i]-'a';if(!c[now][index])c[now][index]=++cnt;now=c[now][index];value[now]++;}loc[id]=now;}int q[N];void buildac(){int head=0,tail=0,now;q[++tail]=1;while(head!=tail){now=q[++head];for(int i=0;i<=25;i++){if(c[now][i]){int k=fail[now];while(!c[k][i]) k=fail[k];fail[c[now][i]]=c[k][i];q[++tail]=c[now][i];}}}for(int i=tail;i>=1;i--)value[fail[q[i]]]+=value[q[i]];}char st[N];int main(){init();scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%s",st),ins(st,i);buildac();for(int i=1;i<=n;i++)printf("%d\n",value[loc[i]]);}
总之,只要搞清楚怎么在这个trie树上去转移,剩下的就看自己dp学的好不好了。。
0 0
- ac自动机及相关dp
- hdu4758 AC自动机+dp
- AC自动机 + DP小结
- AC自动机+DP+hdu2296
- hdu2457 AC自动机+DP
- hdu2296(AC自动机+DP)
- hdu2296 AC自动机+DP
- uvalive4842(AC自动机+DP)
- poj3691(ac自动机+dp)
- hdu3689(ac自动机+dp)
- poj3691(ac自动机+dp)
- AC自动机+dp(CodeForces
- AC自动机相关
- poj 2778 AC自动机DP
- POJ-3691-ac自动机+dp
- hdu 4511 AC自动机 + dp
- POJ1625----AC自动机+大数+DP
- hud 2825 AC自动机dp
- Struts2学习3——数据绑定及获取Session
- 神经网络入门 ,源码7
- wcscpy wcscpy_s strcpy strcpy_s的区别
- 解决Minimum supported Gradle version is 3.3. Current version is 2.14.1问题
- 欢迎使用CSDN-markdown编辑器
- ac自动机及相关dp
- 神经网络入门 ,源码8
- Ubuntu 系统
- 导入csv文件时,由于可能丢失数据,所以无法转换该值
- Activity的生命周期全面分析
- java中static{}语句块详解
- 安装交叉编译工具arm-none-linux-gnueabi-gcc——Linux上编译在android上运行的c程序
- C++11 中值得关注的几大变化(详解)
- 类中成员函数调用问题