zoukankan      html  css  js  c++  java
  • HDU2222 Keywords Search__AC自动机

    Time Limit: 1000MS   Memory Limit: 131072KB   64bit IO Format: %I64d & %I64u

     Status

    Description

    In the modern time, Search engine came into the life of everybody like Google, Baidu, etc. 
    Wiskey also wants to bring this feature to his image retrieval system. 
    Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched. 
    To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match. 
     

    Input

    First line will contain one integer means how many cases will follow by. 
    Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000) 
    Each keyword will only contains characters 'a'-'z', and the length will be not longer than 50. 
    The last line is the description, and the length will be not longer than 1000000. 
     

    Output

    Print how many keywords are contained in the description.
     

    Sample Input

    1 5 she he say shr her yasherhs
     

    Sample Output

    3
     ——————————————————————————————————————————————————————————————————————————————————
    题目大意:
    给定n个子串和一个主串,求有多少个子串在主串中出现过。
    下面的介绍很错略,可参考:http://www.cppblog.com/mythit/archive/2009/04/21/80633.html,本人大多数代码接参考于上面的博客。
    题目解法:AC自动机。
    AC自动机,据说是1975年产生自贝尔实验室。多模式匹配算法。
    知识储备:trie树、KMP算法思想
    操作主要分为三步:
    一:建立字典树(trie)。
    把n个子串构建字典树,节点需要增加一个变量node * fail,即失败指针。
     1 struct node
     2 {
     3     node * fail;
     4     node * next[kind];
     5     int count;
     6     node ()
     7     {
     8         fail=NULL;
     9         memset(next,NULL,sizeof(next));
    10         count=0;
    11     }
    12 };

    二:建立失败指针。

    失败指针分为三类:

    1、root的失败指针指向NULL

    2、root孩子的失败指针指向root

    3、其余节点的失败指针按照以下方法:沿该节点的父亲节点的失败指针查找同样有该节点的节点,把该节点的失败指针指向那个节点的该节点。如果找不到则指向root。如:该节点(1)为‘a',而该节点的父亲节点(2)为’c',则查找'c'的失败指针指向的节点(3),当然节点(3)也为’c',如果节点(3)有’a'这个孩子(节点(4)),则把节点(1)的失败指针指向节点(4),如果节点(3)没有‘a'这个孩子,则沿着沿的失败指针继续查找,直到NULL。则把失败指针指向root。

    建立的方法:

    由于1、2类节点的失败指针一定,而第3指针是沿着父亲的失败指针查找,所以用队列维护指针。

     1 void buildac(node * root)
     2 {
     3     int i;
     4     root->fail=NULL;
     5     q.push(root);
     6     while(!q.empty())
     7     {
     8         node *tp=q.front(),*p;
     9         q.pop();
    10         for(int i=0;i<26;i++)
    11         {
    12             if(tp->next[i]!=NULL)
    13             {
    14                 if(tp==root)tp->next[i]->fail=root;
    15                 else
    16                 {
    17                     p=tp->fail;
    18                     while(p!=NULL)
    19                     {
    20                         if(p->next[i]!=NULL)
    21                         {
    22                             tp->next[i]->fail=p->next[i];
    23                             break;
    24                         }
    25                         p=p->fail;
    26                     }
    27                     if(p==NULL)tp->next[i]->fail=root;
    28                 }
    29                 q.push(tp->next[i]);
    30             }
    31         }
    32     }
    33 }

    三、查询。

    查询方法:指针p指向root,沿着主串的字母走,如果该节点没法走则跳到失败指针再走,如果还不能走则再跳直到到达root。如果某一个点匹配成功则沿失败指针统计对应失败指针的count。

    int query(node *root)
    {
        int i=0,cnt=0,index;
        node *p=root;
        while(s[i])
        {
            index=s[i]-'a';
            while(p->next[index]==NULL && p!=root)p=p->fail;
            p=(p->next[index]==NULL)?root:p->next[index]; 
            node *tp=p;
            while(tp!=root && tp->count!=-1)
            {
                cnt+=tp->count;
                tp->count=-1;
                tp=tp->fail;
            }
            i++;
        }
        return cnt;
    }
    ——————————————————————————————————————————————————————————————————————————————————
      1 #include<cstdio>
      2 #include<cstring>
      3 #include<queue>
      4 
      5 using namespace std;
      6 const int kind=26;
      7 struct node
      8 {
      9     node * fail;
     10     node * next[kind];
     11     int count;
     12     node ()
     13     {
     14         fail=NULL;
     15         memset(next,NULL,sizeof(next));
     16         count=0;
     17     }
     18 };
     19 typedef node * np;
     20 queue<np>q;
     21 char keyw[52],s[1000010];
     22 node * root=NULL;
     23 int t,n;
     24 void ins(char s[],node *root)
     25 {
     26     node *p=root;
     27     int i=0,index;
     28     while(s[i])
     29     {
     30         index=s[i]-'a';
     31         if(!p->next[index])p->next[index]=new node;
     32         p=p->next[index];
     33         i++;
     34     }
     35     p->count++;
     36 }
     37 void buildac(node * root)
     38 {
     39     int i;
     40     root->fail=NULL;
     41     q.push(root);
     42     while(!q.empty())
     43     {
     44         node *tp=q.front(),*p;
     45         q.pop();
     46         for(int i=0;i<26;i++)
     47         {
     48             if(tp->next[i]!=NULL)
     49             {
     50                 if(tp==root)tp->next[i]->fail=root;
     51                 else
     52                 {
     53                     p=tp->fail;
     54                     while(p!=NULL)
     55                     {
     56                         if(p->next[i]!=NULL)
     57                         {
     58                             tp->next[i]->fail=p->next[i];
     59                             break;
     60                         }
     61                         p=p->fail;
     62                     }
     63                     if(p==NULL)tp->next[i]->fail=root;
     64                 }
     65                 q.push(tp->next[i]);
     66             }
     67         }
     68     }
     69 }
     70 
     71 int query(node *root)
     72 {
     73     int i=0,cnt=0,index;
     74     node *p=root;
     75     while(s[i])
     76     {
     77         index=s[i]-'a';
     78         while(p->next[index]==NULL && p!=root)p=p->fail;
     79         p=p->next[index]; 
     80         p=(p==NULL)?root:p; 
     81         node *tp=p;
     82         while(tp!=root && tp->count!=-1)
     83         {
     84             cnt+=tp->count;
     85             tp->count=-1;
     86             tp=tp->fail;
     87         }
     88         i++;
     89     }
     90     return cnt;
     91 }
     92 int main()
     93 {
     94     scanf("%d",&t);
     95     while(t--)
     96     {
     97         root=new node;
     98         scanf("%d",&n);
     99         while(n--)
    100         {
    101             scanf("%s",keyw);
    102             ins(keyw,root);
    103         }
    104         buildac(root);
    105         scanf("%s",s);
    106         printf("%d
    ",query(root));
    107     }
    108     return 0;
    109 }
    View Code
  • 相关阅读:
    vue-router query和params参数的区别
    vue打包成app后,背景图片不显示
    vue打包成app后,点击手机上的物理返回按钮后直接退出app
    Echarts dataZoom 区域缩放
    CSS3 实现别样图型
    Echarts 不能百分比显示或显示有问题
    循环(数组循环、获取json数据循环)、each()循环详解
    页面数据加载完成时,显示loading页面.数据加载完,loading隐藏.
    获取可视区域高度赋值给div(解决document.body.clientHeight的返回值为0的问题)
    移动端rem布局,用户调整手机字体大小或浏览器字体大小后导致页面布局出错问题
  • 原文地址:https://www.cnblogs.com/gryzy/p/6600533.html
Copyright © 2011-2022 走看看