poj2778 & hdu 2243 AC自动机+矩阵 &&BZOJ2938
来源:互联网 发布:易语言和java 编辑:程序博客网 时间:2024/06/08 06:32
AC自动机。
具体可以详看 [AC自动机](http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html)
做这几道题需要注意一点、、如果某节点A的fail指针指向标志节点,那么这个节点A也是标志节点。如图
节点4和节点6肯定是病毒,由于节点3的fail是节点6且是病毒,导致3也是病毒。病毒的递推关系可以传递。
对DNA建立好AC自动机。如果需要找出一条DNA链不包含病毒DNA,那么也就是说这条DNA链一直无法在AC自动机上面进行匹配。也就是不经过标志节点。怎么找出这些DNA链?只要在不是标志的点来回转动就可以了。
那如何找出恰好走了N步的DNA链的数量?
建立好的AC自动机就是一个有向图。那么
x:
把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。 令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。
所以总的来说方法就是:
对模式串建立AC自动机。对建成的AC自动机(有向图)生成矩阵。然后求矩阵快速次幂。。
poj2778代码:
#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int kk=110;struct matrix{ int mat[kk][kk]; int n; matrix(){} matrix(int _n) { n=_n; memset(mat,0,sizeof(mat)); } matrix operator *(const matrix &b)const { matrix ret=matrix(n); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { for(int k=0;k<n;k++) ret.mat[i][j]+=(long long)mat[i][k]*b.mat[k][j]%100000; ret.mat[i][j]%=100000; } return ret; }};matrix q_pow(matrix a,int b){ matrix ret=matrix(a.n); for(int i=0;i<a.n;i++) ret.mat[i][i]=1; while(b) { if(b&1)ret=ret*a; a=a*a; b>>=1; } return ret;}const int maxn=100+10; //字典树可能有的节点数量const int num_son=4;int id(char ch){ if(ch=='A')return 0; if(ch=='T')return 1; if(ch=='C')return 2; if(ch=='G')return 3;}struct Trie{ int next[maxn][num_son],fail[maxn]; bool end[maxn];//分别是字典树、失败指针、是否为完整串标志 int root,l; int newnode() { for(int i=0;i<num_son;i++) next[l][i]=-1; end[l++]=0; //此处 当end[i]>0 意味这个从根到这个点是一个完整的串 return l-1; } void init() { l=0; root=newnode(); } void insert(char buf[]) { int len=strlen(buf); int now=root; for(int i=0;i<len;i++) { if(next[now][id(buf[i])]==-1) next[now][id(buf[i])]=newnode(); now=next[now][id(buf[i])]; } end[now]=1; } void build() //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~ { queue<int>que; fail[root]=root; for(int i=0;i<num_son;i++) if(next[root][i]==-1) next[root][i]=root; else { fail[next[root][i]]=root; que.push(next[root][i]); } while(!que.empty()) { int now=que.front(); que.pop(); if(end[fail[now]]==1) end[now]=1; //传递病毒关系。都是从fail节点(已经访问过)传递。反正就是从上(已经访问过的fail)往下(当前节点)传递。具有传递关系 for(int i=0;i<num_son;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; que.push(next[now][i]); } } } matrix build_matrix() { matrix ret=matrix(l); for(int i=0;i<l;i++) for(int j=0;j<num_son;j++) { if(!end[i]&&!end[next[i][j]]) ret.mat[i][next[i][j]]++; } return ret; } void debug() { for(int i = 0;i <l;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < num_son;j++) printf("%2d",next[i][j]); printf("]\n"); } }}ac;char buf[1000000+10];int main(){ int n,m; while(scanf("%d %d",&n,&m)!=EOF) { ac.init(); for(int i=0;i<n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build();ac.debug(); matrix ac_matrix=ac.build_matrix(); matrix ans=q_pow(ac_matrix,m); int cnt=0; for(int i=0;i<ac.l;i++) cnt+=ans.mat[0][i],cnt%=100000; printf("%d\n",cnt); cout<<ac.l<<endl; } return 0;}
hdu2243:
包含,那就是减法,求出全部的然后减去不包含的。。poj2778就是不包含的。
建立AC自动机,构造矩阵A….
这里困难的地方就是要求《=m长度的总和~~~
不包含的数量就是A^1的第一行sum+A^1的第一行sum+…….A^m的第一行sum……这个直接求有点困难。
对矩阵A新增一行一列。列全为1,行设为0。求出(A^m的第一行sum-1)就是 上诉式子和。
#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int kk=50;struct matrix{ unsigned long long mat[kk][kk]; int n; matrix(){} matrix(int _n) { n=_n; memset(mat,0,sizeof(mat)); } matrix operator *(const matrix &b)const { matrix ret=matrix(n); for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) ret.mat[i][j]+=mat[i][k]*b.mat[k][j]; return ret; }};matrix q_pow(matrix a,int b){ matrix ret=matrix(a.n); for(int i=0;i<a.n;i++) ret.mat[i][i]=1; while(b) { if(b&1)ret=ret*a; a=a*a; b>>=1; } return ret;}const int maxn=50+10; //字典树可能有的节点数量const int num_son=26;struct Trie{ int next[maxn][num_son],fail[maxn]; bool end[maxn];//分别是字典树、失败指针、是否为完整串标志 int root,l; int newnode() { for(int i=0;i<num_son;i++) next[l][i]=-1; end[l++]=0; //此处 当end[i]>0 意味这个从根到这个点是一个完整的串 return l-1; } void init() { l=0; root=newnode(); } void insert(char buf[]) { int len=strlen(buf); int now=root; for(int i=0;i<len;i++) { if(next[now][buf[i]-'a']==-1) next[now][buf[i]-'a']=newnode(); now=next[now][buf[i]-'a']; } end[now]=1; } void build() //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~ { queue<int>que; fail[root]=root; for(int i=0;i<num_son;i++) if(next[root][i]==-1) next[root][i]=root; else { fail[next[root][i]]=root; que.push(next[root][i]); } while(!que.empty()) { int now=que.front(); que.pop(); if(end[fail[now]]==1) end[now]=1; for(int i=0;i<num_son;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; que.push(next[now][i]); } } } matrix build_matrix() { matrix ret=matrix(l+1); for(int i=0;i<l;i++) for(int j=0;j<num_son;j++) { if(!end[i]&&!end[next[i][j]]) ret.mat[i][next[i][j]]++; } for(int i=0;i<l+1;i++) //为了求和 ret.mat[i][l]=1; return ret; } void debug() { for(int i = 0;i < l;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < num_son;j++) printf("%2d",next[i][j]); printf("]\n"); } }}ac;char buf[50];int main(){ int n,m; while(scanf("%d %d",&n,&m)!=EOF) { ac.init(); for(int i=0;i<n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); matrix ac_matrix=ac.build_matrix(); matrix tmp=q_pow(ac_matrix,m); unsigned long long ret=0; for(int i=0;i<tmp.n;i++) ret+=tmp.mat[0][i]; ret--;//ret包含从0次幂到m次幂的和,,,去掉0次幂的 matrix a=matrix(2); a.mat[0][0]=26; a.mat[1][0]=a.mat[1][1]=1; a=q_pow(a,m); unsigned long long ans=a.mat[1][0]+a.mat[0][0]; ans--; cout<<ans-ret<<endl; } return 0;}
BZOJ 2938:
问是否有一个无限长的01序列不包含题目给出的病毒01序列。
其实和poj2778一样。
无限长的01序列无法在AC自动机上面进行匹配。
也就是说这个01序列一直在非标志节点进行循环~~~
只要在这些非标志节点里存在一个环即可。DSF
#include <algorithm>#include <algorithm>#include <iostream>#include<string.h>#include <fstream>#include <math.h>#include <vector>#include <cstdio>#include <string>#include <queue>#include <stack>#include <map>#include <set>#define exp 1e-8#define fi first#define se second#define ll long long#define INF 0x3f3f3f3f#define lson l,mid,rt<<1#define pb(a) push_back(a)#define mp(a,b) make_pair(a,b)#define rson mid+1,r,(rt<<1)+1#define all(a) a.begin(),a.end()#define mm(a,b) memset(a,b,sizeof(a));#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c#define repp(a,b,c)for(int a=b;a>=c;a--)///using namespace std;void bug(string m="here"){cout<<m<<endl;}template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}template<typename __ll>inline void read(__ll &m){READ(m);}template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }const int maxn=30000+10; //字典树可能有的节点数量const int num_son=2;struct Trie{ int next[maxn][num_son],fail[maxn]; bool end[maxn];//分别是字典树、失败指针、是否为完整串标志 int root,l; int vis[maxn]; int newnode() { for(int i=0;i<num_son;i++) next[l][i]=-1; end[l++]=0; //此处 当end[i]>0 意味这个从根到这个点是一个完整的串 return l-1; } void init() { l=0; memset(vis,0,sizeof vis); root=newnode(); } void insert(char buf[]) { int len=strlen(buf); int now=root; for(int i=0;i<len;i++) { if(next[now][buf[i]-'0']==-1) next[now][(buf[i]-'0')]=newnode(); now=next[now][(buf[i]-'0')]; } end[now]=1; } void build() //build了之后 之前的next[][]==-1的 统统转到失败节点的下一个~~~ { queue<int>que; fail[root]=root; for(int i=0;i<num_son;i++) if(next[root][i]==-1) next[root][i]=root; else { fail[next[root][i]]=root; que.push(next[root][i]); } while(!que.empty()) { int now=que.front(); que.pop(); if(end[fail[now]]==1) end[now]=1; for(int i=0;i<num_son;i++) if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; que.push(next[now][i]); } } } void debug() { for(int i = 0;i < l;i++) { printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]); for(int j = 0;j < num_son;j++) printf("%2d",next[i][j]); printf("]\n"); } } bool dfs(int idx) //当前节点、 { if(vis[idx]==2)return 0; for(int i=0;i<num_son;i++) if(vis[next[idx][i]]==1)return 1; else if(vis[next[idx][i]]==0&&!end[next[idx][i]]) { vis[next[idx][i]]=1; if(dfs(next[idx][i])) return 1; } vis[idx]=2; return 0; }}ac;char buf[30000+10];int main(){ int n;read(n); ac.init(); for(int i=0;i<n;i++) { scanf("%s",buf); ac.insert(buf); } ac.build(); //ac.debug(); if(ac.dfs(ac.root))printf("TAK\n"); else printf("NIE"); return 0;}
- poj2778 & hdu 2243 AC自动机+矩阵 &&BZOJ2938
- POJ2778 AC自动机 + 矩阵
- poj2778 ac自动机+矩阵
- POJ2778-AC自动机+矩阵
- poj2778之AC自动机+矩阵快速幂
- poj2778(AC自动机+矩阵快速幂)
- 【POJ2778】AC自动机,DP,矩阵乘法
- poj2778 AC自动机+矩阵快速幂
- poj2778--DNA Sequence(AC自动机+矩阵优化)
- POJ2778---DNA Sequence(AC自动机+矩阵)
- poj2778 DNA Sequence AC自动机+矩阵乘法
- poj2778--DNA Sequence(AC自动机+矩阵优化)
- BZOJ2938【AC自动机】
- bzoj2938(ac自动机)
- POJ2778 DNA Sequence AC自动机+DP+矩阵快速幂
- POJ2778 - AC自动机+非递归的矩阵乘法
- 【poj2778】【AC自动机】【DFA】【矩阵乘法】DNA Sequence
- POJ2778 DNA Sequence AC自动机,DP,矩阵加速
- Erlang学习之基本概念
- 六校模拟 HDU 5389 Zero Escape (DP 人人为我型动态规划)
- HDU 1542 Atlantis (线段树 + 扫描线 + 离散化)
- hdu 1114-Piggy-Bank
- 黑马程序员——Java基础--面向对象(2)
- poj2778 & hdu 2243 AC自动机+矩阵 &&BZOJ2938
- 句子中的引号:把句子中的"改成“”
- EL表达式
- Android中Looper的quit方法和quitSafely方法
- iOS不同传值方法的分享
- JSTL简单入门学习实例
- Linux中fork()函数知识详解
- java实现最小长方形, 判断一个数字中是否包含两个相同的子串, 取石子游戏.
- 内核通知链原理及机制