2011年04月06日

来源:互联网 发布:视频人脸打马赛克软件 编辑:程序博客网 时间:2024/05/17 00:55

题目 http://acm.hdu.edu.cn/showproblem.php?pid=2222

题目大意 :给出字典和文本,看字典中的单词在文本中出现了几个。

考查点 AC自动机

思路 :既然是多串匹配问题,肯定是要用到AC自动机会比较好,但是看了网上很多的AC自动机的解题报告都不是很完整的AC自动机,他们只是建立出来了失败指针,也就是说他们的只是简单的trie+KMP,而没有真正的建立出DFA来,一个DFA应该能够提供有限的字符集组成的串无止尽的在上边运转,应此ac自动机也应该是一个所有节点的字符边都指出去的有向图,而且这个图必然是有环的。而且这个图是饱满的,就拿由小写字母字符集||组成的自动机,那么每个节点的26个指针都应是指出去的,如果这个节点本身就具有某些字符边,那正好,如果没有,就需要我们把虚边加进去,加虚边的时候就会用到后缀指针,也可说是失败指针,因为当我们走到某一位置与当前模式串匹配失败的时候,我们希望的就是能够找到失配位之前的其它模式串与这个模式串具有最长的后缀,那么如果我们是因为字符C匹配失败,我们就可以直接沿着他失败指针的c孩子继续往下走,如果他的失败指针没有c孩子怎么办?别急,在找他的失败指针时,他的失败指针的26的指针我们已经全部建立起来,一次即使他的失败指针没有真正的c孩子,也会有虚边已经建立起来,因此最多就是虚边加回了根点。因为我们规定了根节点出发的虚边都指向自己,且根节点的失败指针是自己,根节点的所有孩子的失败指针是根节点,其它节点(假设是由c指向的节点)的失败指针就按他的最长后缀找,也就是他父节点的失败指针指向节点的c孩子,他的虚边自然就是他失败指针的对应的孩子。

下面给出详细的建图的过程,前提是建立好了trie

[键入文档的引述或关注点的摘要。您可将文本框放置在文档中的任何位置。请使用绘图工具选项卡更改引言文本框的格式。]

l 根结点的后缀结点是它本身。

l 处于trie树第二层的结点的后缀结点也是根结点。

l 对于再往下的某个结点,设它的路径字符串的最后一个字符为c,那么这个结点的后缀为从它在trie树中父结点的后缀结点出发,沿标有c的边走一步后到达的结点。(下文中称从x结点出发,沿标有字符c的边走一步到达的结点为xc孩子

l 按层次遍历trie树,同时:

l 求出每个结点的危险性

l 求出每个结点的后缀结点

l 补齐由它出发的边

l 补边的方法为:

l 从根结点出发的补边指向根本身;

l 对非根结点x,若它没有c孩子,则新建一条边,从x指向x的后缀结点的c孩子。

 

   说明: graph这就是AC_DFA,蓝边是虚边,其实红色节点的虚边也应该建立出来

 

 

 

详细讲解见王赟的论文《trie图的构建、活用与改进》

如果说一般的AC自动机是KMP+trie 的思想,那么我觉得这个AC自动机就是对其的一个改进,在我们充分的利用的我们已经申请的空间的同时,节约了查找时间(匹配时不需要跳跃),这样的操作太完美了。

其中要注意的是如果一个模式串是另一个模式串的子串就会出问题,这个是需要解决的问题,也很好解决就是因为那个模式串是这个模式串的子串,设串为bacdac 那么bacdac指针肯定直接或间接的指向ac,这样我们就可以通过向上递推的看他的失败指针的是否为一个模式串来找到所有的串。这也是ac自动机的神奇之处啊。 据说自动机可以描绘整个世界,向往啊

 

提交情况 ,  memory limitedexceed  若干次

           Runtime error  若干次

           Time limited exceed  若干次

           Wrong answer  若干次

           Accepted 一次

        (错误原因居然是宽搜判重出错,也是因为对trie理解不到位导致)

 

心得体会若干,全在上面了。

 

AC code

#include <stdio.h>

#include <string.h>

 

struct NODE{

   int next[26];

   int suffix;

   int falg;

   int father;

} trie[255555];

int ad;

char T[1000010];

intqueue[255555], boo[255555];

 

voidinsert(){

   int q = 0;

   for(int i = 0; T[i] != '\0'; i ++){

      if(trie[q].next[T[i] - 'a'] == -1){

          trie[q].next[T[i] - 'a'] =ad;

          trie[ad ++].father = q;

      }

      q = trie[q].next[T[i] - 'a'];

   }

   trie[q].falg == -1 ? trie[q].falg = 1 : trie[q].falg ++;

}

 

voidGet_trie_map(){

   int head , tail, i, opt, g,j;

   memset(boo, 0, sizeof(boo));

   head = tail = 0;

   trie[0].suffix = 0;

   boo[0] = 1;

   for(i = 0; i < 26; i++)

      if(trie[0].next[i] == -1)trie[0].next[i] = 0;

      else trie[trie[0].next[i]].suffix =0;

   queue[tail ++] = 0;

   while(head <tail){

      opt = queue[head ++];

      for(i = 0; i < 26; i++)

          if(trie[opt].next[i]> opt &&boo[trie[opt].next[i]] == 0){

             queue[tail ++] = g = trie[opt].next[i];

             boo[g] = 1;

             if(trie[g].suffix == -1)trie[g].suffix = trie[trie[opt].suffix].next[i];

             for(j = 0; j < 26; j++)

                 if(trie[g].next[j] == -1)trie[g].next[j] = trie[trie[g].suffix].next[j];

          }

   }

}

 

voidBuilt_trie(intM){

   int i;

   ad = 1;

   memset(trie, -1, sizeof(trie));

   for(i = 0; i < M; i++){

      scanf("%s", T);

      insert();

   }

   Get_trie_map();

}

 

int Find(){

   int i, q = 0, ans = 0, p,t;

   for(i = 0; T[i] != '\0'; i ++){

      q = trie[q].next[T[i] - 'a'];

      if(trie[q].falg >0){

          ans += trie[q].falg;

          trie[q].falg = 0;

          for(p = q; p > 0; p= trie[p].father)

             for(t = trie[p].suffix;trie[t].falg > 0; t = trie[t].suffix){

                 ans += trie[t].falg;

                 trie[t].falg = 0;

             }

      }

   }

   return ans;

}

 

int main(){

   int M, i, n;

   scanf("%d",&n);

   while(n --){

      scanf("%d",&M);

      Built_trie(M);

      scanf("%s", T);

      printf("%d\n",Find());

   }

   return 0;

}