POJ 2778 AC自动机+矩阵快速幂
来源:互联网 发布:淘宝内部劵骗局 编辑:程序博客网 时间:2024/06/06 04:22
题意
有M个字符串,这M个字符串不能出现在字符串中。要求字符串长度为N,问有多少种方案。
题解
首先,对于这道题,需要知道一个数学定理。对于一张图的邻接矩阵,邻接矩阵的N次幂就是两点距离为N的路径条数。
知道了上述定理以后,这道题就可以看作,字典树从0开始,到某个节点,路径长度为N的方案个数。因为存在不能出现的字符串,结合AC自动机便可解决该题。利用AC自动机对不能出现的字符串,以及后缀子串是不能出现的字符串的节点进行标记。(因为在字典树上,如果想从0开始访问这个节点,那就必须要构成危险字符,才能访问这个节点。)在构建矩阵的时候,被标记的节点值为0。
构建矩阵完成后,利用矩阵快速幂方法进行运算。最后矩阵第一行的和即为所求。
补充说明
事实上,把AC自动机的边映射到矩阵上整个过程详细分析的话还是比较难的。大概可以这样理解。例如拿“ACAGAAA”和”ACAG”进行分析。首先的话,第一个字符我们可以选择A或者GCT,这样的话便有矩阵[3,1,0,0,0,0,0,0]。如果我们要选择第二个字符的话,我们可以看一下,每个节点转移到0状态的路径矩阵[3,2,3,1,0,2,2,0]T。两个矩阵相乘,所代表的意义就是从0出发,经过某个点,再回到0。(从0出发是必要的,但是再回到0不是必要的,因此最后相乘的结果存放在第一行的第一列,第一行其他列代表不回到0的情况)这样依此类推,便可以解决问题。
当然,有一些细节还是要说明一下。比如说ACAGAAA,我们可以看到,转移到0状态的路径矩阵为[3,2,3,1,0,2,2,0]T,也就是说倒数第二个A和倒数第三个A也是有能力转移到0点的。这样的话,会不会存在不符合条件的字符串转移到了0点的情况呢?
仔细想一下的话,就会发现这种情况是不存在的。因为如果想要转移到0点,首先就要到达这个点,换句话说就是初始第一行[3,1,0,0,0,0,0,0],经过某些变换后,要能使倒数第二个数和倒数第三个数变为1以上,这样的话才有可能进行转移。
我们可以分析一下,我们可以首先打印一下6号点的转移情况(矩阵第七列),看看谁能转移到6号点,我们发现矩阵为[0,0,0,0,0,1,0,0],在这里我们发现,5号点有一条边可以转移到6号点。(也就是我们已经选择了A,再选一个A)然后,我们再打印一下5号点的矩阵,看看谁能转移到5号点,结果发现是[0,0,0,0,0,0,0,0]。我们发现,4号点和5号点有边相连。原本如果处于四号点状态,再选一个A就可以达到5号点。这时候,对四号点进行处理的作用就显现出来了。对四号点进行过滤,使得四号点不能进行任何状态转移,也不允许任何点转移到四号状态,意义就在于此。
我们可以尝试对状态矩阵进行一千次快速幂运算,结果正如我们所想的。0-3号点都不为0,4号点以后都为0。这也就意味着,0-3号点,都可以进行转移,组成字符串。
上述就是我对利用矩阵+AC自动机计算字符串组成方案的一些思考,主要就是为了验证矩阵+AC自动机算法的正确性。如有错误,还望赐教。
代码
#include <iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<vector>#include<cmath>#include<queue>#include<string>#include<set>#include<map>#include<bitset>#include<stack>#define UP(i,l,h) for(int i=l;i<h;i++)#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)#define W(a) while(a)#define MEM(a,b) memset(a,b,sizeof(a))#define INF 0x3f3f3f3f3f3f3f3f#define LL long long#define MAXN 10000#define EPS 1e-10#define MOD 100000#define N 2using namespace std;typedef LL MATRIX[110][110];int ch[110][4];char fea[15];int len,sz;int val[110];int f[110];MATRIX a,temp,ans;int getNum(char c) { if(c=='A') { return 0; } if(c=='C') { return 1; } if(c=='T') { return 2; } if(c=='G') { return 3; }}void insert() { len=strlen(fea); int u=0; UP(i,0,len) { int x=getNum(fea[i]); if(!ch[u][x]) { ch[u][x]=sz++; } u=ch[u][x]; }// cout<<u<<endl; val[u]=1;}void getFail() { queue<int> q; MEM(f,0); UP(x,0,4) { if(ch[0][x]) { q.push(ch[0][x]); } } int s=0; W(!q.empty()) { int r=q.front(); q.pop(); UP(x,0,4) { int u=ch[r][x]; if(!u) { ch[r][x]=ch[f[r]][x]; continue; } q.push(u); int v=f[r]; f[u]=ch[v][x]; if(val[f[u]]==1) { val[u]=1; } } }}void buildMatrix() { MEM(a,0); UP(i,0,sz) { UP(j,0,4) { if(val[i]||val[ch[i][j]]) {// cout<<i<<" "<<j<<" "<<val[i]<<" "<<ch[i][j]<<endl; continue; } a[i][ch[i][j]]++; } }}void matrixMulti(MATRIX a,MATRIX b) { MEM(temp,0); UP(i,0,sz) { UP(j,0,sz) { UP(k,0,sz) { temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;// cout<<temp[i][j]<<endl; } } } memcpy(a,temp,sizeof(temp));}void quick(int n) { MEM(ans,0); UP(i,0,sz){ ans[i][i]=1; } W(n){ if(n&1){ matrixMulti(ans,a); } matrixMulti(a,a); n>>=1; }}LL getAns(){ LL sum=0; UP(i,0,sz){ sum+=ans[0][i]; } return sum;}int main() { int m,n; W(~scanf("%d%d",&m,&n)) { MEM(val,0); MEM(ch,0); sz=1; UP(i,0,m) { scanf("%s",fea); insert(); } getFail(); buildMatrix(); quick(n); printf("%I64d\n",getAns()%MOD); }}/*2 20000000000ACGC*/
- poj 2778 AC自动机+矩阵快速幂
- poj 2778 AC 自动机 + 矩阵快速幂
- POJ 2778 ac自动机+矩阵快速幂
- poj 2778 AC自动机 + 矩阵快速幂
- poj 2778 AC自动机+矩阵快速幂
- poj 2778(ac自动机+矩阵快速幂)
- AC自动机+矩阵快速幂 POJ 2778
- POJ 2778 (AC自动机 矩阵快速幂)
- POJ-2778 ac自动机+矩阵快速幂
- POJ 2778 AC自动机+矩阵快速幂
- POJ 2778:DNA Sequence(AC自动机+矩阵快速幂)
- poj 2778 DNA Sequence 【ac自动机 + dp + 矩阵快速幂】
- poj 2778 AC自动机+DP+矩阵快速幂
- POJ 2778 DNA Sequence【AC自动机+矩阵快速幂】
- poj 2778 (AC自动机+dp+矩阵快速幂)
- poj 2778 DNA Sequence(AC自动机+矩阵快速幂)
- [AC自动机+dp+矩阵快速幂] poj 2778 DNA Sequence
- poj 2778 ac自动机dp矩阵快速幂
- windows 端口被进程占用 但是进程已经关闭
- 模拟鼠标操作
- JAVA多线程详细
- Qt阴影效果
- HDOJ 3018-Ant Trip
- POJ 2778 AC自动机+矩阵快速幂
- java面向对象的各种关系总结(UML)
- 关于递归的总结——汉诺塔、素因数的求解(Python实现)
- 2.多级菜单
- HDU6092 Rikka with Subset【DP】
- 关于AndroidStudio的快捷键使用
- CodeForces
- 排序算法五——堆排序
- 8.9上课感悟