zoukankan      html  css  js  c++  java
  • hihocoder1036 trie图

    trie图用于解决多模式匹配问题。设有N个长度不超过L的模式串,匹配串长为M,那么用trie图解决多模式匹配问题的复杂度为O(N*L+M).

    思路:

    trie图的基础是trie树。

    1.用trie树实现多模式匹配

      首先建立N个模式串的trie树。设匹配串为s,我们枚举匹配起始位置i,在trie树中依次去查询字典中是否有字符串&str[i]的前缀。这样做的复杂度为O(M*L).

    2.用trie图实现多模式匹配

      对于上述用trie树实现多模式匹配的方式,当我们沿trie树匹配的过程中找不到当前字符对应的边的时候,我们的做法是改变起始位置i的值,从trie树根节点从头再来。

      但其实此时我们已经从s的当前起始点i开始成功匹配了k个字符,那么我们在枚举下一个起点i+1的时候,意味着前k-1个字符已经在上一次枚举中匹配过了,就像kmp算法一样,如果我们能利用好这个信息,就能大大的减小时间复杂度。

    2.1后缀节点的概念

      假设从根节点到当前节点A的路径所对应的字符串为"abcde",那么"bcde"所对应的路径末节点就是节点A的后缀节点。但是,trie树中可能并不存在"bcde"这样一条从根节点开始的路径,此时A的后缀节点是"bcde"的后缀节点,即后缀节点的后缀节点,如此递归。特别的,根节点的后缀节点为根节点,深度为1的节点的后缀节点也为根节点。

    2.2后缀节点的求法

      采用广度优先遍历来求解后缀节点,这样,当我们要求trie树中第i层节点的后缀节点的时候,第0~i-1层的所有点的后缀节点都已知了。我们可以根据父节点来求当前节点的后缀节点:假设父节点到当前节点的边为ch,那么当前节点的后缀节点就是父节点的后缀节点通过ch这样一条边所到达的节点。然而,父节点的后缀节点有可能并不存在ch这样一条边,此时,我们需要看后缀节点的后缀节点是否存在ch这样一条边,如此递归。但是,如果我们每次都这样递归来做未免显得有些麻烦,所以,在bfs的时候,对于每个节点,我们用一个数组记录下从当前节点经过每一个ch所到达的节点,这里的“到达”的理解:当存在ch边的时候,就是一般意义的到达,当不存在ch边的时候,这里的“到达”应该理解为其后缀节点经过ch这样一条边所到达的节点。如此一来,后缀节点的求解就很简单了。

      还有一个问题需要注意:当当前节点不存在匹配字符所对应的边即不匹配的时候,我们直接跳转到其后缀节点,然后继续往下匹配,我们这种做法会可能造成一种后果:那就是从根节点到后缀节点的路径中可能存在标记节点,而被我们忽略了。解决这个问题的方法是“在求解当前后缀节点的过程中,若求得的后缀节点是标记节点,那么当前节点也要被标记”。

    2.3实现多模式匹配

      求得了所有的后缀节点,相当于我们的trie图已经构建完毕了。此时,多模式匹配问题,就只需要遍历匹配串,从trie树的根节点开始逐个字符匹配,当匹配不成功的时候,就跳转到后缀节点继续匹配,直到匹配成功或者匹配串遍历结束。

    我的代码:

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <queue>
      5 using namespace std;
      6 
      7 #define MAXN 1000005
      8 #define MAX_CH 26
      9 
     10 char str[MAXN];
     11 int nodecnt;
     12 
     13 struct trieNode
     14 {
     15     trieNode *p[MAX_CH]; 
     16     trieNode *pFather;
     17     int ch; 
     18     bool isMarked;
     19     trieNode *postfix;
     20     trieNode *next[MAX_CH];
     21     
     22     void init()
     23     {
     24         for(int i=0; i<MAX_CH; ++i)
     25         {
     26             p[i] = NULL;
     27             next[i] = NULL;
     28         }
     29         isMarked = 0;
     30         postfix = NULL;
     31     }
     32 }node[MAXN];
     33 
     34 struct Trie
     35 {
     36     trieNode *root;
     37     
     38     void init()
     39     {
     40         root = &node[0];
     41         root->init();
     42         root->pFather = NULL;
     43         root->ch = -1;
     44         
     45         nodecnt = 1;
     46     }
     47     
     48     //向trie中插入单词 
     49     void insert(char *str)
     50     {
     51         trieNode *pNow = root;
     52         int len = strlen(str);
     53         for(int i=0; i<len; ++i)
     54         {
     55             int id = str[i]-'a';
     56             if(pNow->p[id]==NULL)
     57             {
     58                 pNow->p[id] = &node[nodecnt++];
     59                 pNow->p[id]->init();
     60                 pNow->p[id]->pFather = pNow;
     61                 pNow->p[id]->ch = id;
     62             }
     63             if(i==len-1)
     64             {
     65                 pNow->p[id]->isMarked = 1;
     66             }
     67             pNow = pNow->p[id];
     68         }
     69     }
     70     
     71     //求某个节点的后缀节点 
     72     void getPostfix(trieNode *pNow)
     73     {
     74         if(pNow==root)
     75         {
     76             pNow->postfix = pNow;
     77             
     78             for(int i=0; i<MAX_CH; ++i)
     79             {
     80                 if(pNow->p[i]!=NULL)
     81                 {
     82                     pNow->next[i] = pNow->p[i];
     83                 }
     84                 else
     85                 {
     86                     pNow->next[i] = pNow;
     87                 }
     88             }
     89         }
     90         else
     91         {
     92             if(pNow->pFather==root)
     93             {
     94                 pNow->postfix = root;
     95             }
     96             else
     97             {
     98                 pNow->postfix = pNow->pFather->postfix->next[pNow->ch];
     99             }
    100             if(pNow->postfix->isMarked)
    101             {
    102                 pNow->isMarked = 1;
    103             }
    104             
    105             for(int i=0; i<MAX_CH; ++i)
    106             {
    107                 if(pNow->p[i]!=NULL)
    108                 {
    109                     pNow->next[i] = pNow->p[i];
    110                 }
    111                 else
    112                 {
    113                     pNow->next[i] = pNow->postfix->next[i];
    114                 }
    115             }
    116         }    
    117     }
    118     
    119     //广度优先遍历,求所有节点的后缀节点, 构造trie图 
    120     void bfs()
    121     {
    122         queue<trieNode*> q;
    123         q.push(root);
    124         while(!q.empty())
    125         {
    126             trieNode *pNow = q.front();
    127             getPostfix(pNow);
    128             q.pop();
    129             for(int i=0; i<MAX_CH; ++i)
    130             {
    131                 if(pNow->p[i]!=NULL) q.push(pNow->p[i]);
    132             }
    133         }    
    134     }
    135     
    136     //多模式匹配 
    137     bool match(char *str)
    138     {
    139         trieNode *pNow = root;
    140         int len = strlen(str);
    141         
    142         for(int i=0; i<len; ++i)
    143         {
    144             int id = str[i]-'a';
    145             if(pNow->p[id]==NULL)
    146             {
    147                 pNow = pNow->postfix;
    148                 if(pNow->isMarked) return 1;
    149             }
    150             pNow = pNow->next[id];
    151             if(pNow->isMarked) return 1;
    152         }
    153         return 0;
    154     }
    155 }trie;
    156 
    157 int main()
    158 {
    159     int n;
    160     while(scanf("%d", &n)!=EOF)
    161     {
    162         trie.init();
    163         while(n--)
    164         {
    165             scanf("%s", str);
    166             trie.insert(str);
    167         }
    168         trie.bfs();
    169         scanf("%s", str);
    170         puts(trie.match(str)?"YES":"NO");
    171     }
    172     return 0;
    173 }

    题目来源:http://hihocoder.com/problemset/problem/1036

  • 相关阅读:
    python3 crypto winrandom import error
    Flask-Babel 中文支持(zh-CN和zh-Hans-CN)
    pip 安装psycopg的错误
    Aapache status / apache2ctl status 总是403
    为什么你还在用嵌入式的方式来使用mod_wsgi?
    Git中当add错误的时候怎么办?
    Python 内置彩蛋
    本人AI知识体系导航
    本人SW知识体系导航
    SSH密钥对登录的原理和实践
  • 原文地址:https://www.cnblogs.com/pczhou/p/4295189.html
Copyright © 2011-2022 走看看