AC自动机小试 POJ2778

http://poj.org/problem?id=2778

题目翻译:给定m个异常基因片段,在所有长度为N的DNA序列中如‘AAAAA....’,'ATTTTT....','ATCG...','CGTA...',.....中不包含异常序列的DNA个数mod 100000(十万)的值

这题题解是AC自动机和矩阵乘法,首先建立AC自动机

我们先看AC自动机建立过程(会的跳过,不会的英语好的看这篇英文论文:http://wenku.baidu.com/view/8b6f6d85ec3a87c24028c467.html,不会的英文不好的看不懂论文的往下看)

首先定义node为记录类型

{   son[1..4]of int;code:char;fail:longint;danger:boolean;

    son,fail初始为-1或nil或null,danger:=false;

     }

定义函数

function anti(a:char):longint;

begin  case a of

            'A':exit(1);
        'C':exit(2);
        'G':exit(3);
        'T':exit(4);
        end;

end;

1.建立Trie树,即字母树

2.初始化第一层节点fail指针为root节点,将第一层节点加入队列中

3.取队首元素x,将所有x的son入队

4.for y ∈ x.son

       begin

            temp:=x.fail;

            while (temp>0)or(temp<>root) do

                 begin

                     for z ∈ temp.son

                        if z.code=y.code

                           then flag:=true,break;

                     if flag then temp:=z,break;

                     temp:=temp.fail;

                 end;

           if  temp=root

             then

                  for z ∈ root.son

                        if z.code=y.code

                           then temp:=z,break;

            y.fail:=temp;

       end;

5.Complete Build AC Automaton

注意到一个经典的矩阵乘法题,给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值

答案是将有向图的邻接矩阵乘k次输出G[A,B]

我们可以把A点看做root点,走n步不经过异常节点的方案数mod p(p=100000)的值

那么很明显一点是x节点的son中的异常节点(设为y)是不可能由x转移到的

题中每个节点最多有4个son

如果某个x.son为nil呢

设z=x.fail,当前son为i

由于x.fail为x.son匹配失败时的最长后缀在trie中的位置,那么以fail指针往上(fail指针总是指向上面)寻找z.son[i]不为nil的z节点后终止

然后判断z.son[i]是否为异常节点,不是则g[x,z.son[i]]++或者若z=root且root.son[i]=nil 则g[x,root]++;

按照trie的bfs序列

取当前元素x

for i:=1 to 4 do

  if x.son[i]=-1

    then

      begin

            z:=x.fail

            while (z<>root)and(z.son[i]=-1) do

                 z:=z.fail;

            if  (z=root)and(root.son[i]=-1)

              then g[x,root]++

             else

                if   (z.son[i]<>-1)and(not z.son[i].danger)

                  then g[x,z.son[i]]++;

      end

   else if  not x.son[i].danger then g[x,x.son[i]]++;        

然后就是将g矩阵乘n次,将第root行加起来mod 100000 就是ans

代码pascal

program poj2778;
type    matrix=array[0..101,0..101]of int64;
const   mo=100000;
var     a:string;nodes,leng,i,j,point,m,n,top,x,tail,som,k:longint;ans:int64;
        b,g:matrix;
        son:array[0..101,0..4]of longint;
        off:array[0..101]of longint;
        code:array[0..101]of char;
        stack:array[0..110]of longint;
        comp:array[0..110]of boolean;
procedure muti(a,b:matrix;var c:matrix);
begin   fillchar(c,sizeof(c),0);
        for i:=0 to nodes do
          for j:=0 to nodes do
            begin
              for k:=0 to nodes do
                c[i,j]:=(a[i,k]*b[k,j]+c[i,j])mod mo;

            end;
end;
function anticode(a:char):longint;
begin   case a of
        'A':exit(1);
        'C':exit(2);
        'G':exit(3);
        'T':exit(4);
        else exit(0);
        end;
end;
begin
        readln(m,n);
        fillchar(son,sizeof(son),$ff);
        for i:=1 to m do
          begin
            readln(a);
            leng:=length(a);
            point:=0;
            for j:=1 to leng do
              begin
                if son[point,anticode(a[j])]<>-1
                  then
                    point:=son[point,anticode(a[j])]
                  else
                    begin
                      inc(nodes);
                      son[point,anticode(a[j])]:=nodes;
                      code[nodes]:=a[j];
                      point:=nodes;
                    end;
              end;
            comp[point]:=true;
          end;
        top:=0;
        tail:=1;
        while top<tail do
          begin
            inc(top);
            x:=stack[top];
            for i:=1 to 4 do
              if son[x,i]<>-1
                then
                  begin
                    inc(tail);
                    stack[tail]:=son[x,i];
                    if x=0 then continue;
                    som:=off[x];
                    repeat
                      if son[som,i]<>-1
                        then
                          begin
                            som:=son[som,i];
                            break;
                          end;
                      som:=off[som];
                    until som<=0;
                    if som=0
                      then
                        if son[som,i]<>-1
                          then
                            som:=son[som,i];
                    if comp[som]
                      then comp[son[x,i]]:=true;
                    off[son[x,i]]:=som;
                  end;
          end;
        top:=0;nodes:=0;
        while top<tail do
          begin
            inc(top);
            x:=stack[top];
            if comp[x] then continue;
            if x>nodes then nodes:=x;
            for i:=1 to 4 do
              if son[x,i]=-1
                then
                  begin
                    som:=off[x];
                    while (som>0)and(son[som,i]=-1) do
                      begin
                        som:=off[som];
                      end;
                    if (son[som,i]<>-1)and(not comp[son[som,i]])
                      then
                        inc(g[x,son[som,i]])
                      else
                        if (som=0)and(son[som,i]=-1)
                          then
                            inc(g[x,0]);
                  end
                else
                  if (son[x,i]<>-1)and(not comp[son[x,i]])
                    then
                      inc(g[x,son[x,i]]);
          end;
        for i:=0 to nodes do
          b[i,i]:=1;
        while (n>0) do
          begin
            if (n and 1)>0
              then
                muti(b,g,b);
            n:=n>>1;
            muti(g,g,g);
          end;
        for i:=0 to nodes do
          ans:=(ans+b[0,i])mod mo;
        writeln(ans);
end.

另一篇题解: http://hi.baidu.com/%D2%D5%C1%D6010/blog/item/6db06ccf0a3b440993457e7b.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值