【poj2778-DNA Sequence】AC自动机+矩阵乘法
题意:
(只含AGCT)给定m个病毒串,让你构造一个长度为n的字符串(也只含有AGCT),问有多少种方案。
n很大:1<=n<=2000000000
题解:
用病毒串建立AC自动机(num个节点),然后构建一个num*num的矩阵表示节点i走一步到j有多少种方案。注意:根节点也要算。
原理:原本是在AC自动机上不断地走,但不能走到病毒末端。该矩阵每格(i,j)表示i走一步到j有多少种方案,n次方就是i走n步到j有多少种方案。
最后把矩阵[0][i]全部加起来,就是从0出发走n步的全部方案。
为什么可以只用AC自动机上的节点?因为走其他节点就相当于回到根节点,假设给定一个新构造的串要你判断是不是有病毒串,也不需要除了病毒串以外的节点来判断。
注意要用longlong。
代码如下
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; const int N=,M=,Mod=;
typedef long long LL;
int num,n,m;
char s[];
queue<int> q;
struct node{
int bk,fail,son[];
}a[];
struct nd{
int x,y;
LL z[][];
}map; // void output(nd p)
// {
// for(int i=0;i<=p.x;i++)
// {
// for(int j=0;j<=p.y;j++)
// printf("%d ",p.z[i][j]);
// printf("\n");
// }
// printf("\n");
// } int idx(char c)
{
if(c=='A') return ;
if(c=='T') return ;
if(c=='C') return ;
if(c=='G') return ;
} void clear(int x)
{
a[x].fail=a[x].bk=;
memset(a[x].son,,sizeof(a[x].son));
} void trie(char *c)
{
int x=,l=strlen(c);
for(int i=;i<l;i++)
{
int ind=idx(c[i]);
if(!a[x].son[ind])
{
num++;
clear(num);
a[x].son[ind]=num;
}
x=a[x].son[ind];
}
a[x].bk=;
} void buildAC()
{
while(!q.empty()) q.pop();
q.push();
while(!q.empty())
{
int x=q.front();q.pop();
int fail=a[x].fail;
for(int i=;i<=;i++)
{
if(a[x].son[i])
{
int y=a[x].son[i],z=a[fail].son[i];
a[y].fail=x ? z : ;
a[y].bk|=a[z].bk;
q.push(y);
}
else a[x].son[i]=a[fail].son[i];
if(!a[a[x].son[i]].bk) map.z[x][a[x].son[i]]++;
} }
} nd multi(nd u,nd v)
{
nd ans;
ans.x=u.x;ans.y=v.y;
memset(ans.z,,sizeof(ans.z));
for(int i=;i<=ans.x;i++)
for(int j=;j<=ans.y;j++)
for(int k=;k<=ans.y;k++)
ans.z[i][j]=(ans.z[i][j]+u.z[i][k]*v.z[k][j])%Mod;
return ans;
} LL dp()
{
nd ans;
ans.x=num,ans.y=num;
memset(ans.z,,sizeof(ans.z));
for(int i=;i<=num;i++) ans.z[i][i]=;
while(n)
{
if(n&) ans=multi(ans,map);
map=multi(map,map);
n/=;
}
LL sum=;
for(int i=;i<=num;i++)
sum=(sum+ans.z[][i])%Mod;
return sum;
} int main()
{
freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d%d",&m,&n);
for(int i=;i<=m;i++)
{
scanf("%s",s);
trie(s);
}
map.x=num,map.y=num;
memset(map.z,,sizeof(map.z));
buildAC();
printf("%I64d\n",dp());
return ;
}
很久以前刚学打过一次,以前的代码如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std; const int M=;
const int MOD=(int)1e5;
char s[M];
int q[M];
long long ay[M][M],sum[M][M];
int n,m,tot,len;
struct node
{
int son[],cnt,fail,mark;
}t[M]; void floy()
{
tot=;
for(int i=;i<=M;i++)
{
t[i].cnt=;t[i].mark=;
for(int j=;j<=;j++) t[i].son[j]=;
}
memset(q,,sizeof(q));
memset(ay,,sizeof(ay));
memset(sum,,sizeof(sum));
} void read_trie()
{
tot=;
int i,j;
scanf("%d%d",&m,&n);
for(i=;i<=m;i++)
{
int x,ind;
scanf("%s",s+);
len=strlen(s+);
x=;
for(j=;j<=len;j++)
{
if(s[j]=='A') ind=;
if(s[j]=='C') ind=;
if(s[j]=='T') ind=;
if(s[j]=='G') ind=; if(!t[x].son[ind]) t[x].son[ind]=++tot;
x=t[x].son[ind];
t[x].cnt++;
if(j==len) t[x].mark=;
}
}
} void build_AC()
{
int i,j,x,y;
q[++q[]]=;
for(i=;i<=q[];i++)
{
x=q[i];
y=t[x].fail;
for(j=;j<=;j++)
{
if(t[x].son[j])
{
//t[t[x].son[j]].fail=t[y].son[j];
//while(y && !t[y].son[j]) y=t[y].fail;
t[t[x].son[j]].fail=x?t[y].son[j]:;
if(t[t[t[x].son[j]].fail].mark) t[t[x].son[j]].mark=;
q[++q[]]=t[x].son[j];
}
else t[x].son[j]=t[y].son[j];
if(!t[t[x].son[j]].mark) ++ay[x][t[x].son[j]];
}
}
} void MatrixMult(long long a[M][M],long long b[M][M])
{
int i,j,k;
long long c[M][M]={};
for(i=;i<=tot;i++)
for(j=;j<=tot;j++)
for(k=;k<=tot;k++)
c[i][j]+=a[i][k]*b[k][j];
for(i=;i<=tot;i++)
for(j=;j<=tot;j++)
a[i][j]=c[i][j]%MOD;
} long long MatrixPow(int k)
{
int i,j;
for(i=;i<=tot;i++)
for(j=;j<=tot;j++)
sum[i][j]=(i==j);
while(k)
{
if(k&) MatrixMult(sum,ay);
MatrixMult(ay,ay);
k>>=;
}
for(int i=;i<=tot;i++)
{
for(int j=;j<=tot;j++)
printf("%d ",sum[i][j]);
printf("\n");
}
long long ans=;
for(i=;i<=tot;i++) ans+=sum[][i];
return ans%MOD;
} int main()
{
freopen("a.in","r",stdin);
// freopen("b.out","w",stdout);
floy();
read_trie();
build_AC(); printf("%I64d\n",MatrixPow(n));
return ;
}
poj2778(以前的代码)
【poj2778-DNA Sequence】AC自动机+矩阵乘法的更多相关文章
- POJ 2778 DNA Sequence (AC自动机,矩阵乘法)
题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. #include<iostream> #includ ...
- [poj2778]DNA Sequence(AC自动机+矩阵快速幂)
题意:有m种DNA序列是有疾病的,问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列.(仅含A,T,C,G四个字符) 解题关键:AC自动机,实际上就是一个状态转移图,注意能少取模就少取模, ...
- POJ2778 DNA Sequence(AC自动机 矩阵)
先使用AC自动机求得状态转移关系,再建立矩阵,mat[i][j]表示一步可从i到j且i,j节点均非终止字符的方案数,则此矩阵的n次方表示n步从i,到j的方法数. #include<cstdio& ...
- poj2778 DNA Sequence(AC自动机+矩阵快速幂)
Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's ve ...
- POJ2278 DNA Sequence —— AC自动机 + 矩阵优化
题目链接:https://vjudge.net/problem/POJ-2778 DNA Sequence Time Limit: 1000MS Memory Limit: 65536K Tota ...
- 【距离GDOI:128天】【POJ2778】DNA Sequence(AC自动机+矩阵加速)
已经128天了?怎么觉得上次倒计时150天的日子还很近啊 ....好吧为了把AC自动机搞透我也是蛮拼的..把1030和这道题对比了无数遍...最终结论是...无视时间复杂度,1030可以用这种写法解. ...
- [poj2778 DNA Sequence]AC自动机,矩阵快速幂
题意:给一些字符串的集合S和整数n,求满足 长度为n 只含charset = {'A'.'T‘.'G'.'C'}包含的字符 不包含S中任一字符串 的字符串的种类数. 思路:首先对S建立ac自动机,考虑 ...
- poj 2778 DNA Sequence ac自动机+矩阵快速幂
链接:http://poj.org/problem?id=2778 题意:给定不超过10串,每串长度不超过10的灾难基因:问在之后给定的长度不超过2e9的基因长度中不包含灾难基因的基因有多少中? DN ...
- 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
题目描述 输入 注意 是0<=P 输出 样例输入 样例输出 题解 AC自动机+矩阵乘法 先将所有字符串放到AC自动机中,求出Trie图. 然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向 ...
- DNA Sequence - POJ 2778(AC自动机+矩阵乘法)
题目大意:DNA序列是有 ATGC 组成的,现在知道一些动物的遗传片段有害的,那么如果给出这些有害的片段,能否求出来所有长度为 N 的基因中有多少是不包含这些有害片段的. 分析:也是断断续续做了一 ...
随机推荐
- UIAlertController——之Block回调
iOS8.0之后出现的提示框 =.=,比自己去改block回调要好.
- iOS学习之Object-C语言集合遍历和数组排序
一.集合遍历 1.集合:OC中提供的容器类,数组,字典,集合. 2.遍历:对集合中元素依次取出的过程叫做遍历. 二.for循环遍历 1.通过for循环的循环变量用作数组元 ...
- Swift 类构造器的使用
Swift 中构造器需要遵循的规则还是很多的, 总结一下, 有以下规则: 调用相关 指定构造器必须调用它直接父类的指定构造器方法. 便利构造器必须调用同一个类中定义的其它初始化方法. 便利构造器在最后 ...
- bootsrap check 获取选中
代码如下: <label> <input type="checkbox" name="PartEdge2" value="false ...
- QT 按钮(4种样式)
// 1.正常 btnNormal_ = new QPushButton("Normal Button", this); // 2.可停驻 btnCheck_ = new Q ...
- ubuntu 修改主机名
sudo gedit /etc/hostname sudo gedit /etc/hosts
- 加速传感器(CoreMotion) swift
// // ViewController.swift // UILabelTest // // Created by mac on 15/6/23. // Copyright (c) 2015年 fa ...
- Mac下的常用Shell命令
今天介绍一下在Mac的终端中一些常用的Shell命令: 1.查看当前工作目录的完整路径 pwd (pwd的原意是:print work directiory,而不是密码password的意思,呵呵) ...
- php7+apache的环境安装配置
因为刚开始接触php,所以要对php的开发环境进行搭建. 1.首先到Apache的官网下载最新版: http://httpd.apache.org/download.cgi: 参照该网址配置Apach ...
- adb怎么判断是否有root权限,并更改system/app内容
一.首先判断root权限: adb root 结果: C:\signapp>adb root restarting adbd as root # 说明有root权限 ,若是adbd cannot ...