POJ2778 DNA sequence
2015-12-19 12:43
316 查看
题目大意:给出m个疾病基因片段(m<=10),每个片段不超过10个字符。求长度为n的不包含任何一个疾病基因片段的DNA序列共有多少种?(n<=2000000000)
分析:本题需要对m个疾病基因片段构建一个AC自动机,自动机中的每个节点表示一个状态。其中AC自动机中的叶子节点表示的是病毒,所以是非法状态。同时,如果某个节点到根的字符串的后缀是一个病毒,那么该节点也是非法状态。剔除掉所有的非法状态,那么剩下的节点都表示合法状态了。然后用节点的nxt指针表示状态之间转化关系。若nxt[i]==0,则nxt[i]指针指向当前节点fail指针的nxt[i],如果仍然为0,则nxt[i]指向根节点。这样处理以后,每个指针都不会指向0。这样,该自动机可以看做是一个合法状态的转换图,节点表示各种合法状态,边表示添加一个字符将转换为另一个状态。于是我们可以得到一个矩阵。该矩阵实际上表示该状态图的邻接矩阵。对该矩阵自乘n次。最后结果矩阵的第1行各元素之和表示从空状态添加n个字符能够得到的所有合法状态的数量。
矩阵的思想非常巧妙。
分析:本题需要对m个疾病基因片段构建一个AC自动机,自动机中的每个节点表示一个状态。其中AC自动机中的叶子节点表示的是病毒,所以是非法状态。同时,如果某个节点到根的字符串的后缀是一个病毒,那么该节点也是非法状态。剔除掉所有的非法状态,那么剩下的节点都表示合法状态了。然后用节点的nxt指针表示状态之间转化关系。若nxt[i]==0,则nxt[i]指针指向当前节点fail指针的nxt[i],如果仍然为0,则nxt[i]指向根节点。这样处理以后,每个指针都不会指向0。这样,该自动机可以看做是一个合法状态的转换图,节点表示各种合法状态,边表示添加一个字符将转换为另一个状态。于是我们可以得到一个矩阵。该矩阵实际上表示该状态图的邻接矩阵。对该矩阵自乘n次。最后结果矩阵的第1行各元素之和表示从空状态添加n个字符能够得到的所有合法状态的数量。
矩阵的思想非常巧妙。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define MAXN 102 #define MAXL 12 #define MAXC 4 #define MOD 100000 struct node { int fail,nxt[6],flag; }trie[MAXN]; int head,tail,myq[MAXN],root=1,tot=1; char word[MAXL]; int degree; int a[MAXN][MAXN],b[MAXN][MAXN],c[MAXN][MAXN],(*ans)[MAXN]; void multi(int (*a)[MAXN],int (*b)[MAXN],int (*c)[MAXN]) { for(int i=1;i<=degree;i++) { for(int j=1;j<=degree;j++) c[i][j]=0; } for(int i=1;i<=degree;i++) { for(int j=1;j<=degree;j++) { for(int k=1;k<=degree;k++) { c[i][j]+=(long long)a[i][k]*b[k][j]%MOD; c[i][j]%=MOD; } } } } void power(int (*t1)[MAXN],int h) { for(int i=1;i<=degree;i++) for(int j=1;j<=degree;j++) b[i][j]=0; for(int i=1;i<=degree;i++) b[i][i]=1; int (*t2)[MAXN],(*t3)[MAXN]; t2=b,t3=c; while(h) { if(h&1) {multi(t1,t2,t3); swap(t2,t3); } h>>=1; multi(t1,t1,t3); swap(t1,t3); } if(t2!=a) { memcpy(a,t2,sizeof a); } } int inline getid(char C) { if(C=='A')return 0; else if(C=='T')return 1; else if(C=='C')return 2; else return 3; } void insert(int r,char *s) { int len=strlen(s); for(int i=0;i<len;i++) { int val=getid(s[i]); if(trie[r].nxt[val]==0) trie[r].nxt[val]=++tot; r=trie[r].nxt[val]; } trie[r].flag=1;//1表示结束节点 } void build(int r) { trie[r].fail=r; myq[tail++]=r; int ch; while(head<tail) { r=myq[head++]; for(int i=0;i<MAXC;i++) { ch=trie[r].nxt[i]; if(ch)myq[tail++]=ch; if(r==root) { if(ch) trie[ch].fail=root; else trie[r].nxt[i]=root; } else { if(ch) {trie[ch].fail=trie[trie[r].fail].nxt[i]; trie[ch].flag|=trie[trie[ch].fail].flag; } else trie[r].nxt[i]=trie[trie[r].fail].nxt[i]; } ch=trie[r].nxt[i]; if(trie[ch].flag!=1) a[r][ch]++; } } } int main() { int n,m; scanf("%d%d",&m,&n); for(int i=0;i<m;i++) { scanf("%s",word); insert(root,word); } build(root); degree=tot; power(a,n); int ans=0; for(int i=1;i<=degree;i++) {ans+=a[1][i]; ans%=MOD; } printf("%d\n",ans); }
相关文章推荐
- Lipshitz Sequence Codeforces Round #333 (Div. 1) B(单调栈)
- iOS UILabel 设置行间距
- Arduino - 28BYJ-48步进电机+ULN2003驱动模块
- 小米手机如何刷开发版MIUI 小米4/小米Note稳定版升级开发版方法
- 打印 上一主题 下一主题 简单讲解UEFI及MBR,及UEFI系统安装引导修复
- UIDatePicker 时间滚动表
- SQLServer跨服务器访问数据库(openrowset/opendatasource/openquery)
- juery实现粘贴复制
- [IOS]轻松理解UITableViewCell的重用机制
- 308. Range Sum Query 2D - Mutable
- Android:Handler+Looper+MessageQueue+Thread(线程间的通信)随记
- 【Java学习笔记】String、StringBuffer和StringBuilder
- mysql update中使用subquery
- 【POJ 3709】K-Anonymous Sequence
- jenkins 安装 SVN Publisher 后向 svn 提交代码报错: E170001: Authentication required for...
- Query on a tree II
- 2016扁平化图标设计如何做太原UI设计师郭文军讲
- ERROR: Error installing rails: ERROR: Failed to build gem native extension.
- Leetcode: Implement Stack using Queues
- Storyboard应用:快速创建及设置View