zoukankan      html  css  js  c++  java
  • HDU 2222 关键词查找

    题目大意:给出一篇文章,长度最多1000000,若干个关键词,关键词有可能重复。关键词不超过10000,每个关键词不超过50个字符。请问该文章包含多少个关键词。

    这是AC自动机的入门题。首先将关键词分别插入到一棵trie树中,对每个关键词的对应的结束节点设置一个标记;第二步设置trie树中节点的fail指针。每个节点u都得有fail指针,指向某一个节点v,表示当u无法继续往下匹配时,u应该跳转的节点。节点u与v的字符相同,且根到v的字符串为根到u的字符串的最长后缀。如果u没有找到对应的v,则u的fail指针指向root。root的fail指针指向自身;第三步则是查询。查询时从根节点开始。每次根据当前的字符在当前节点的儿子中找到对应节点,如果没有找到,则跳转到当前节点的fail指针处,一直要找到某个节点,在它儿子中存在和当前字符匹配的节点,然后继续往下。当然如果到了根节点也没有找到,则查找下一个字符了。这里要注意的是,即使当前节点可以继续往下匹配,在当前节点往下匹配之前,我们需要另外拿一个指针,从当前节点出发,通过fail指针往上遍历,筛选是否有结束节点。因为有些关键词可能是当前节点到根的字符串的后缀。每个节点被筛选后可以做一个标记,下次筛选到这里时可以提前退出了。这样可以保证每个节点最多被筛选一次。

    具体见代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define MAXC 26
    struct node
    {
      int cnt,fail,nxt[MAXC];       
    }trie[550000];
    int root,tot=1,myq[550000],res=0,T,n,head,tail;
    char artical[1000005],word[55];
    void insert(int r,char *s)
    {
       int len=strlen(s),val;
       for(int i=0;i<len;i++)
       {
          val=s[i]-'a';
          if(trie[r].nxt[val]==0)
            trie[r].nxt[val]=++tot;
           r=trie[r].nxt[val];       
       }
       trie[r].cnt++;
    }
    void build(int r)
    {
      trie[r].fail=r;
      myq[tail++]=r;
      while(head<tail)
      {
       r=myq[head++];
      for(int i=0;i<MAXC;i++)
      {
         int ch,p;
         if(ch=trie[r].nxt[i])
         {
           myq[tail++]=ch;
           for(p=trie[r].fail;p!=root&&trie[p].nxt[i]==0;p=trie[p].fail);
           int tmp=trie[p].nxt[i];//tmp可能为空,因为p可能为根节点。
           if(tmp&&tmp!=ch)trie[ch].fail=tmp;//防止ch的fail指针指向自己
           else trie[ch].fail=root;
         }
      }
    }
    }
    void query(int r,char *s)
    {
         int len=strlen(s),val;
         for(int i=0;i<len;i++)
         {
            val=s[i]-'a';
            while(r!=root&&trie[r].nxt[val]==0)
              r=trie[r].fail;
            r=trie[r].nxt[val];
            if(r==0)r=root;
            for(int temp=r;temp!=root;temp=trie[temp].fail)//查询时要另一个指针从当前节点通过fail往上找。
            {
            if(trie[temp].cnt==-1)break; //凡是找过的节点cnt设置为-1.所以遇到,可以提前退出。
              res+=trie[temp].cnt;
              trie[temp].cnt=-1;
            }
         }
    }
    int main()
    {
       scanf("%d",&T);
       while(T--)
       {
         root=1;
         head=tail=0;
         tot=1;
         res=0;
         memset(trie,0,sizeof trie);
         scanf("%d",&n);
         for(int i=0;i<n;i++)
         {scanf("%s",word);
          insert(root,word);
         }  
          scanf("%s",artical);       
         build(root);
         query(root,artical);
         printf("%d
    ",res);
       }
       return 0;
    }
  • 相关阅读:
    操作系统--进程间同步
    操作系统--进程间通信
    LeetCode-- Unique Binary Search Trees II
    STL源码--序列容器(一)
    操作系统--用户级线程和内核级线程
    非洲孩子
    寻找最大数(三)
    找点
    心急的C小加
    1044 拦截导弹——http://codevs.cn/problem/1044/
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/5050765.html
Copyright © 2011-2022 走看看