题目链接:
[摘自Matrix67] 题目大意是,检测所有可能的n位DNA串有多少个DNA串中不含有指定的病毒片段。合法的DNA只能由ACTG四个字符构成。题目将给出10个以内的病毒片段,每个片段长度不超过10。数据规模n<=2 000 000 000。
下面的讲解中我们以ATC,AAA,GGC,CT这四个病毒片段为例,说明怎样像上面的题一样通过构图将问题转化为例题8。我们找出所有病毒片段的前缀,把n位DNA分为以下7类:以AT结尾、以AA结尾、以GG结尾、以?A结尾、以?G结尾、以?C结尾和以??结尾。其中问号表示“其它情况”,它可以是任一字母,只要这个字母不会让它所在的串成为某个病毒的前缀。显然,这些分类是全集的一个划分(交集为空,并集为全集)。现在,假如我们已经知道了长度为n-1的各类DNA中符合要求的DNA个数,我们需要求出长度为n时各类DNA的个数。我们可以根据各类型间的转移构造一个边上带权的有向图。例如,从AT不能转移到AA,从AT转移到??有4种方法(后面加任一字母),从?A转移到AA有1种方案(后面加个A),从?A转移到??有2种方案(后面加G或C),从GG到??有2种方案(后面加C将构成病毒片段,不合法,只能加A和T)等等。这个图的构造过程类似于用有限状态自动机做串匹配。然后,我们就把这个图转化成矩阵,让这个矩阵自乘n次即可。最后输出的是从??状态到所有其它状态的路径数总和。
题目中的数据规模保证前缀数不超过100,一次矩阵乘法是三方的,一共要乘log(n)次。因此这题总的复杂度是100^3 * log(n),AC了。
Example:
2 3
AG
CG
状态转移图:
要注意当前前缀 i 匹配的前缀 j 可能不是可行的前缀。
1 //STATUS:C++_AC_313MS_624KB 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include
q; 94 mem(mta.ma,0); 95 f[0]=0; 96 sta[0]=size++; 97 for(c=0;c<4;c++){ 98 u=ch[0][c]; 99 if(u){100 f[u]=0;q.push(u);101 if(!val[u]){102 sta[u]=size++;103 mta.ma[0][sta[u]]++;104 }105 }106 else mta.ma[0][0]++;107 }108 while(!q.empty()){109 r=q.front();q.pop();110 if(sta[r]==-1)continue;111 for(c=0;c<4;c++){112 u=ch[r][c];113 if(!u){114 v=ch[r][c]=ch[f[r]][c];115 if(sta[v]==-1)continue;116 mta.ma[sta[r]][sta[v]]++;117 }118 else {119 q.push(u);120 v=f[r];121 while(v && !ch[v][c])v=f[v];122 f[u]=ch[v][c];123 if(val[u] || sta[f[u]]==-1)continue;124 sta[u]=size++;125 mta.ma[sta[r]][sta[u]]++;126 }127 }128 }129 }130 131 int main()132 {133 // freopen("in.txt","r",stdin);134 int i,j;135 LL sum;136 char s[20];137 idx['A']=0;idx['C']=1;idx['T']=2,idx['G']=3;138 while(~scanf("%d%d",&m,&n))139 {140 init();141 for(i=0;i