hdu3962 Microgene

Microgene

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 382 Accepted Submission(s): 223


Problem Description
sevenzero is very interesting in Bioinformation and have done some research on it. One day, sevenzero found a phenomenon called Microgene. Microgene is a special fragment in the DNA, and different Microgenes may have the same hereditary effect. Microgene works if and only if there are more than one Microgenes(Microgenes may overlap) with the same hereditary effect in the DNA. To finish his paper, sevenzero wants to know how many different DNAs with length L which contain the hereditary effect caused by Microgenes.

To simplify the problem, a DNA or a Microgene is considerd as a string consisting of character 'A', 'T', 'C' and 'G'. And a Microgene is in the DNA if the Microgene string is the substring of the DNA string. All Microgenes given are different and with the same hereditary effect.

Input
There are several test cases in the input. Each case begins with a line with an integer N (1 ≤ N ≤ 6) and L (1 ≤ N ≤ 1000000), denoting the number of Microgenes and the length of DNA. The following N lines contain N strings representing the Microgenes.The length of the Microgene is no more than 5. The input is terminated by EOF.

Output
One line for each case, the answer modulo 10007.

Sample Input
 
 
2 3 AT TC 2 3 ATC T 3 1000000 ATCG TCGT CTAG

Sample Output
 
 
1 11 5063

Source

Recommend

We have carefully selected several similar problems for you: 3961 3969 3963 3964 3965


题解:

太强了,真是太强了,我到现在还是不懂这道题的矩阵快速幂在干什么。

直接复制网上的题解吧。。。

题目大意:给定m个DNA病毒序列,求碱基构成的长度为n且含有两个以上DNA病毒序列,结果对10007取模。

解题思路:本题代码量大,较为综合,需用到AC自动机改造而成的Trie图、DP思想、矩阵快速幂。

如果n比较小,那么本题可以用DP解,由于题目明显的有三个状态,未含病毒串、含一个病毒串,含两个及两个以上病毒,根据这三个就可以写出一个状态转移方程。但是本题可以简化一下,先求出总的组合种数,再减去含有一个病毒串和未含病毒串的种数就是解了。那么状态就只有2个。

状态转移方程为:if (j->next 为病毒串) dp[i+1][j->next][1] += dp[i][j][0] ;

else if (j->next非病毒串) dp[i+1][j->next][1] += dp[i][j][1];

dp[i+1][j->next][0] += dp[i][j][0];

但是本题n特别大,必须用矩阵进行优化。先将Trie图转化为一个(total * 2) * (total * 2)(total为总节点数)可达矩阵,如果i < total,那说明这个节点和他的后缀不含有病毒串,如果i > total,那说明这个节点和他的后缀含有1个病毒串。

具体实现是这样的,if (i->next->flag) matrix[i][i->next+total]++;

else matrix[i][i->next]++,matrix[i+total][i->next+total]++;

这样,矩阵就被分成四块相当于四个象限,第2个象限(i和j都小于total)怎么走都不会出现病毒串,那么经过A^n,他们的值就是最后病毒序列为0个的种数。第1个象限表示i走到j-total会出现一个病毒DNA序列,第四个象限i-total走到j-total,原来含1个病毒串现在还是1个。



代码(这个比较浅显易懂):



const mo=10007;
type node=record danger:boolean;
                                 fail:longint;
                                 son:array[1..4]of longint;
                    end;
         arr=array[0..100,0..100]of longint;
var
    b,bb,c:arr;
    q:array[0..100]of longint;
    a:array[0..100]of node;
    s:string;
    i,j,tot,n,m,ans,sum:longint;
function number(ch:char):longint;
begin
  case ch of
  'A':number:=1;
  'T':number:=2;
  'C':number:=3;
  'G':number:=4;
  end;
end;
procedure add;
var
    i,t,len,u:longint;
begin
  len:=length(s);
  t:=1;
  for i:=1 to len do
   begin
     u:=number(s[i]);
     if a[t].son[u]=0 then
      begin
        inc(tot);
        fillchar(a[tot],sizeof(node),0);
        a[t].son[u]:=tot;
      end;
      t:=a[t].son[u];
   end;
  a[t].danger:=true;
end;
procedure setac;
var
    p,l,r,u:longint;
begin
  l:=0;r:=1;q[1]:=1;
  while l<r do
   begin
     inc(l);
     u:=q[l];
     a[u].danger:=a[u].danger or a[a[u].fail].danger;
     for i:=1 to 4 do
      begin
        if a[u].son[i]<>0 then
         begin
           inc(r);
           q[r]:=a[u].son[i];
         end;
        p:=a[u].fail;
        while(p>0)and(a[p].son[i]=0)do p:=a[p].fail;
        if p=0 then a[a[u].son[i]].fail:=1
         else a[a[u].son[i]].fail:=a[p].son[i];
      end;
   end;
end;
procedure cheng(var a,b:arr);
var
    i,j,k:longint;
begin
  fillchar(c,sizeof(c),0);
  for i:=1 to 2*tot do
   for j:=1 to 2*tot do
    for k:=1 to 2*tot do
     c[i,j]:=(c[i,j]+(a[i,k]*b[k,j])mod mo)mod mo;
  a:=c;
end;
procedure quick(k:longint);
begin
  if k=1 then exit;
  quick(k shr 1);
  cheng(b,b);
  if k and 1=1 then
   cheng(b,bb);
end;
procedure quick1(k:longint);
begin
  if k=1 then exit;
  quick1(k shr 1);
  sum:=(sum*sum)mod mo;
  if k and 1=1 then
   sum:=(sum*4)mod mo;
end;
procedure make_matrix;
var
    p,u:longint;
begin
  fillchar(b,sizeof(b),0);
  for i:=1 to tot do
   begin
     for j:=1 to 4 do
     begin
      p:=i;
      while (p>0)and(a[p].son[j]=0)do p:=a[p].fail;
      if p=0 then
       begin
        if not a[i].danger then
         begin
           inc(b[tot+i,1+tot]);
           inc(b[i,1]);
         end
        else inc(b[i,tot+1]);
       end
      else
       begin
         u:=a[p].son[j];
         if not a[i].danger then
          begin
           inc(b[tot+i,u+tot]);
           inc(b[i,u]);
          end
          else inc(b[i,u+tot]);
       end;
     end;
   end;
  {for i:=1 to 2*tot do
  begin
   for j:=1 to 2*tot do
    write(b[i,j],' ');
   writeln;
  end;}
  bb:=b;
  quick(m);
  {for i:=1 to 2*tot do
  begin
   for j:=1 to 2*tot do
    write(b[i,j],' ');
   writeln;
  end;}
  sum:=4;
  quick1(m);
  ans:=0;
  for i:=1 to tot do
    ans:=(ans+b[1,i])mod mo;
  for i:=tot+1 to 2*tot do
    if not a[i-tot].danger then
     ans:=(ans+b[1,i])mod mo;
  writeln(((sum-ans)mod mo+mo)mod mo);
end;
begin
   while not seekeof do
   begin
      readln(n,m);
      tot:=1;
      fillchar(a[1],sizeof(node),0);
      for i:=1 to n do
       begin
        readln(s);
        add;
       end;
      setac;
      make_matrix;
   end;
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值