热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

poj2778DNASequence(AC自动机+矩阵快速幂)

DNASequence题意在由‘A’,‘C’,‘T’,‘G’组成的DNA序列中有一些基因片段携带遗传病。如果某个DNA序列中包含一个或多个这样的基因片段,那么这个D

DNA Sequence

在这里插入图片描述


题意

在由‘A’, ‘C’ ,‘T’ ,‘G’ 组成的DNA序列中有一些基因片段携带遗传病 。如果某个DNA序列中包含一个或多个这样的基因片段 ,那么这个DNA序列是有遗传病的 。

现在给 m 个基因片段 si 。问所有长度为 n 的DNA序列中 ,有多少个没有遗传病 ,答案模100000 。

&#xff08;其中 0<&#61; m,si ,<&#61; 10 , 1 <&#61; n <&#61; 2000000000&#xff09;


input

4 3
AT
AC
AG
AA


output

36


思路

这题一个很巧妙的地方是用到了矩阵快速幂来处理这个非常大的n。
离散数学学过一个东西 ------ 对一个图的邻接矩阵求n次幂后 &#xff0c; aij 的值代表从 i 号节点通过n步走到 j 号节点所用的方案数 。&#xff0c;这正是我们所需要的。
将m个字串构建成带fail指针的 Trie 树 &#xff0c;将字符串的结束位置打上标记 &#xff08;和普通的AC自动机一样&#xff09;。那么没有遗传病的长度为n的DNA序列 &#xff0c; 就是我们从根节点开始走n步且不经过标记点的所有走法。 于是&#xff0c;对于建好的trie树&#xff0c;如果当前节点和后续节点都没有被标记&#xff0c;那就将它们连一条边&#xff0c;存在邻接矩阵里。将邻接矩阵取n次幂&#xff0c;将所有的 a[root][i] 加起来即可。
这题要注意一点&#xff0c;做矩阵乘法时不要次次取模 &#xff0c; 而是处理完一行再取模&#xff0c;否则会TLE。

样例的图大概长这样&#xff08;灵魂画法.jpg) &#xff1a;
在这里插入图片描述


代码

#include
#include
#include
#include
using namespace std;
long long n,m;
const int mod &#61; 100000;
string ss[15];
map<char,int> func;
struct trie{int nxt[2050][4]; // kuangbin的板子int fail[2050];bool end[2050]; int idx,root;int newnode(){for(int i&#61;0;i<4;i&#43;&#43;)nxt[idx][i] &#61; -1;end[idx] &#61; false;idx&#43;&#43;;return idx-1;}void init(){idx &#61; 0;root &#61; newnode();}void insert(string buf){int len &#61; buf.length();int now &#61; root;for(int i&#61;0;i<len;i&#43;&#43;){if(nxt[now][func[buf[i]]]&#61;&#61;-1)nxt[now][func[buf[i]]] &#61; newnode();now &#61; nxt[now][func[buf[i]]];}end[now] &#61; true; // 字符串结束的位置打上标记}void build(){queue<int> q;fail[root] &#61; root;for(int i&#61;0;i<4;i&#43;&#43;){if(nxt[root][i]&#61;&#61;-1)nxt[root][i] &#61; root;else{fail[nxt[root][i]] &#61; root;q.push(nxt[root][i]);}}while(!q.empty()){int now &#61; q.front();q.pop();for(int i&#61;0;i<4;i&#43;&#43;){if(nxt[now][i]&#61;&#61;-1)nxt[now][i] &#61; nxt[fail[now]][i];else{fail[nxt[now][i]] &#61; nxt[fail[now]][i];q.push(nxt[now][i]);}//注意AC自动机中末尾节点会连回失配指针的位置&#xff0c;即 nxt[now][i] &#61; nxt[fail[now]][i]// 所以此时得对nxt[now][i] 是否应该被标记再做一次判断end[nxt[now][i]] |&#61; end[nxt[fail[now]][i]]; }}}
};
trie tr;
// 矩阵快速幂
struct matrix{long long a[105][105]; matrix() {memset(a, 0, sizeof(a));}void print(){cout<<"now"<<endl;for(int i&#61;0;i<&#61;tr.idx;i&#43;&#43;){for(int j&#61;0;j<&#61;tr.idx;j&#43;&#43;){cout<<a[i][j]<<" ";}cout<<endl;}}
};
matrix operator*(const matrix &x,const matrix &y){matrix mx;for (int i &#61; 0; i <&#61; tr.idx; i&#43;&#43;){for (int j &#61; 0; j <&#61; tr.idx; j&#43;&#43;){for (int k &#61; 0; k <&#61; tr.idx; k&#43;&#43;){mx.a[i][j] &#61; (mx.a[i][j] &#43; x.a[i][k] * y.a[k][j]);}mx.a[i][j] %&#61;mod; //处理完一行再取模}}return mx;
}
matrix ksm(matrix ax,long long b){matrix res;for(int i&#61;0;i<&#61;tr.idx;i&#43;&#43;) res.a[i][i] &#61; 1;while(b){if (b & 1) res &#61; res * ax;ax &#61; ax * ax;b >>&#61; 1;}return res;
}int main(){ios::sync_with_stdio(false);cin>>n>>m;tr.init();func[&#39;A&#39;] &#61; 0,func[&#39;C&#39;] &#61; 1,func[&#39;T&#39;] &#61; 2,func[&#39;G&#39;] &#61; 3;for(int i&#61;1;i<&#61;n;i&#43;&#43;){cin>>ss[i];tr.insert(ss[i]);}tr.build();matrix ans;for(int i&#61;0;i<&#61;tr.idx;i&#43;&#43;){for(int j&#61;0;j<4;j&#43;&#43;){if(tr.end[i]&#61;&#61;0&&tr.end[tr.nxt[i][j]]&#61;&#61;0){ans.a[i][tr.nxt[i][j]]&#43;&#43;; //邻接矩阵连边}}}ans &#61; ksm(ans,m);int sum &#61; 0;for(int i&#61;0;i<&#61;tr.idx;i&#43;&#43;){sum &#61; (int)(sum&#43;ans.a[0][i]%mod)%mod;}cout<<sum%mod<<endl;return 0;
}

推荐阅读
  • 本文深入探讨了POJ2762问题,旨在通过强连通分量缩点和单向连通性的判断方法,解决有向图中任意两点之间的可达性问题。文章详细介绍了算法原理、实现步骤,并附带完整的代码示例。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • C++: 实现基于类的四面体体积计算
    本文介绍如何使用C++编程语言,通过定义类和方法来计算由四个三维坐标点构成的四面体体积。文中详细解释了四面体体积的数学公式,并提供了两种不同的实现方式。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • Splay Tree 区间操作优化
    本文详细介绍了使用Splay Tree进行区间操作的实现方法,包括插入、删除、修改、翻转和求和等操作。通过这些操作,可以高效地处理动态序列问题,并且代码实现具有一定的挑战性,有助于编程能力的提升。 ... [详细]
  • 本题旨在通过给定的评级信息,利用拓扑排序和并查集算法来确定全球 Tetris 高手排行榜。题目要求判断是否可以根据提供的信息生成一个明确的排名表,或者是否存在冲突或信息不足的情况。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
author-avatar
水门街口卖瓜子的
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有