POJ 2778(AC自动机+矩阵快速幂)
传送门
题面:
DNA Sequence
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 19507 | Accepted: 7431 |
Description
It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For example, if a animal's DNA sequence contains segment ATC then it may mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don't contain those segments.
Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G,and the length of sequences is a given integer n.
Input
First line contains two integer m (0 <= m <= 10), n (1 <= n <=2000000000). Here, m is the number of genetic disease segment, and n is the length of sequences.
Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10.
Output
An integer, the number of DNA sequences, mod 100000.
Sample Input
4 3
AT
AC
AG
AA
Sample Output
36
题意:
给你m个由‘A’','C','G','T'组成的模板字符串,问你长度为n的由上述4个字符组成的所有字符串中,有多少个字符串是不含有模板串的有多少个。
题目分析:
神题!!
首先,倘若这个问题中要组成的字符串的长度m比较小的话,题目的做法就类似bzoj1030,我们设dp[i][j]为当前长度为i的字符串处于Trie树中的第j号结点所具有的方案数,就可以通过转移方程dp[i][next[j][k]+=dp[i-1][j]求解。
但是在这个问题中,字符串的长度多达2e9,显然在这个长度下甚至连一个dp数组都开不下,显然用dp去做显然不太现实。因此我们考虑从图的角度审视这个问题。
我们分析之前所构造出的转移方程,dp[i+1][j]+=dp[i][next[j][k]],我们发现这个转移方程在Trie图上的表示为:结点j能够到达结点next[j][k],即两个结点在Trie图上是可达的。
而我们题目中所要求的长度为n的方案数,实质上等价于要求我们从Trie图的根节点开始转移,一直转移n次,且保证转移到的结点不是结尾字符所得到的方案数。
我们上面也谈到了,"能否转移"在Trie图上的表示为"是否可达",因此"能够转移的方案数"也即为"可达的方案数"。
因此,这个题的问题就被巧妙的转化成了:从Tire图的root结点出发,走n步且不经过结尾字符所能够达到的结点的个数。
而对于这个问题,在离散数学,我们可以知道:要求出一个图中第i个点与第j个点的可达关系,我们可以通过将这张图的邻接矩阵A求T次幂,求幂的结果即为第i个点走T步达到结点j的个数
因此上诉的问题就可以转化成一个矩阵的问题去用矩阵快速幂进行求解!因此我们只需要先构建出Trie图,并将Trie图的邻接矩阵A构造出来,最后通过矩阵快速幂求出即可!
代码:
//#include <bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
#include <queue>
#define maxn 110
using namespace std;
const int mod=100000;
int nn,mm;
char st[maxn];
struct Marix{//矩阵
int mo[maxn][maxn],n;
Marix(){}
Marix(int _n){
n=_n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) mo[i][j]=0;
}
}
};
Marix mul(Marix a,Marix b){//矩阵乘法
Marix res=Marix(a.n);
for(int i=0;i<a.n;i++){
for(int j=0;j<a.n;j++){
for(int k=0;k<a.n;k++){
int tmp=(long long )a.mo[i][k]*b.mo[k][j]%mod;
res.mo[i][j]=(res.mo[i][j]+tmp)%mod;
}
}
}
return res;
}
Marix powMod(Marix a,int n){//矩阵快速幂
Marix nul;
nul=Marix(a.n);
for(int i=0;i<nul.n;i++){
nul.mo[i][i]=1;
}
while(n){
if(n&1) nul=mul(nul,a);
a=mul(a,a);
n>>=1;
}
return nul;
}
map<char,int>mp;
struct Trie{//AC自动机
int next[maxn][4],fail[maxn],End[maxn],root,id;
int newnode(){
for(int i=0;i<4;i++){
next[id][i]=-1;
}
End[id]=0;
return id++;
}
void init(int nn){
mp['A']=0,mp['C']=1,mp['T']=2,mp['G']=3;
id=0;
root=newnode();
}
void Insert(char *str){
int now=root;
int len=strlen(str);
for(int i=0;i<len;i++){
if(next[now][mp[str[i]]]==-1){
next[now][mp[str[i]]]=newnode();
}
now=next[now][mp[str[i]]];
}
End[now]=1;
}
void build(){
queue<int>que;
for(int i=0;i<4;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();
for(int i=0;i<4;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]);
}
}
End[now]|=End[fail[now]];
}
}
Marix getMarix(){//得到Trie图中的邻接可达性矩阵
Marix Mar=Marix(id);
for(int i=0;i<id;i++){
for(int j=0;j<4;j++){
if(End[next[i][j]]) continue;
Mar.mo[i][next[i][j]]++;
}
}
return Mar;
}
}ac;
int main()
{
scanf("%d%d",&nn,&mm);
ac.init(nn);
for(int i=0;i<nn;i++){
scanf("%s",st);
ac.Insert(st);
}
ac.build();
Marix M=ac.getMarix();
Marix tmp=powMod(M,mm);
int res=0;
for(int i=0;i<tmp.n;i++){
res=(res+tmp.mo[0][i])%mod;
}
cout<<res<<endl;
}