• hiho一下 第二周&第四周:从Trie树到Trie图


    hihocoder #1014 题目地址:http://hihocoder.com/problemset/problem/1014

    hihocoder #1036 题目地址:  http://hihocoder.com/problemset/problem/1036

     

    trie图其实就是trie树+KMP

     

    #1014trie树

    #include<stdio.h>
    #include <algorithm>
    #include <cstring>
    #include <string.h>
    #include <iostream>
    #include <list>
    #include <map>
    #include <set>
    #include <stack>
    #include <string>
    #include <utility>
    #include <vector>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    
        typedef struct Trie_node
        {
            int count;                    // 统计单词前缀出现的次数
            struct Trie_node* next[26];   // 指向各个子树的指针
            bool exist;                   // 标记该结点处是否构成单词
        }TrieNode , *Trie;
    
    Trie createTrieNode()
    {
        TrieNode* node = (TrieNode *)malloc(sizeof(TrieNode));
        node->count = 0;
        node->exist = false;
        memset(node->next , 0 , sizeof(node->next));    // 初始化为空指针
        return node;
    }
    
    void Trie_insert(Trie root, char* word)
    {
        Trie node = root;
        char *p = word;
        int id;
        while( *p )
        {
            id = *p - 'a';
            if(node->next[id] == NULL)
            {
                node->next[id] = createTrieNode();
            }
            node = node->next[id];  
            ++p;
            node->count += 1;      // 包括统计每个单词出现的次数
        }
        node->exist = true;        // 可以构成一个单词
    }
    
    int Trie_search(Trie root, char* word)
    {
        Trie node = root;
        char *p = word;
        int id;
        while( *p )
        {
            id = *p - 'a';
            node = node->next[id];
            ++p;
            if(node == NULL)
                return 0;
        }
        return node->count;
    }
    
    int main()
    {
        Trie root = createTrieNode();     // 字典树的根节点
        char str[12] ;
        bool flag = false;
        int n ,m ;
        scanf ("%d", &n);
        for( int i =0 ; i < n ; i++)
            {
                scanf ("%s", str);
                Trie_insert(root , str);
            }
        scanf ("%d", &m);
        for( int i =0 ; i < m ; i++)
            {
                scanf ("%s", str);
                printf("%d
    ",Trie_search(root , str));
            }
        return 0;
    }

    #1036trie图

    其实就是trie树+KMP

     

    数据结构与trie树一样,加了一个prev指针,作用类似于KMP的失配函数next[]

    Trie_insert函数不变

    添加一个构造prev的函数Trie_build()。

    prev指针的作用:在匹配失败时跳转到具有公共前缀的字符继续匹配,类似于KMP的失配函数next[]。

    利用bfs构造prev指针。

    指针prev指向与字符p相同的结点,如果没有与p前缀相同的节点,则指向root

    根节点的前缀是根节点

    最后字符匹配的Trie_search()函数类似于KMP的过程,在当前字符匹配失败时,利用prev指针跳转到具有最长公共前后缀的字符继续匹配。

    #include<stdio.h>
    #include <algorithm>
    #include <cstring>
    #include <string.h>
    #include <iostream>
    #include <list>
    #include <map>
    #include <set>
    #include <stack>
    #include <string>
    #include <utility>
    #include <queue>
    #include <vector>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    
        typedef struct Trie_node
        {
            int count;                    // 统计单词前缀出现的次数
            struct Trie_node* next[26];
            bool exist;                   // 标记该结点处是否构成单词
            struct Trie_node* prev; //前缀节点
        }TrieNode , *Trie;
    
    Trie createTrieNode()
    {
        TrieNode* node = (TrieNode *)malloc(sizeof(TrieNode));
        node->prev=NULL;
        node->count = 0;
        node->exist = false;
        memset(node->next , 0 , sizeof(node->next));
        return node;
    }
    
    void Trie_insert(Trie root, char* word)
    {
        Trie node = root;
        char *p = word;
        int id;
        while( *p )
        {
            id = *p - 'a';
            if(node->next[id] == NULL)
            {
                node->next[id] = createTrieNode();
            }
            node = node->next[id];
            ++p;
            node->count += 1;      // 统计每个单词出现的次数
        }
        node->exist = true;        // 单词结束的地方标记
    }
    
    void Trie_build(Trie root)        //Trie树和Tie图的区别就在于此,类似于KMP构造失配函数的一个过程
    {
        queue<Trie> Q;    //利用bfs构造prev指针,队列实现BFS
        Trie node=root;    
        for(int i=0;i<26;i++)//根节点的子节点的rev都是根节点,根节点的prev也是根节点
        {
            if(node->next[i]!=NULL)
            {
                node->next[i]->prev=root;
                Q.push(node->next[i]);
            }
        }
        while(!Q.empty())
        {
            node=Q.front();
            Q.pop();
            for(int i=0; i<26; i++)
            {
                Trie p=node->next[i];
                if(p!=NULL&&p->exist==false) //若此处能构成单词则不用处理prev
                {
                    Trie prev=node->prev; //上一个结点的前缀节点
                    while(prev)
                        {
                            if(prev->next[i]!=NULL)
                            {
                                p->prev=prev->next[i]; //prev指向与字符p相同的结点
                                if(p->prev->exist==true)
                                    p->exist=true;
                                break;
                            }
                            else
                                prev=prev->prev;
                        }
    
                    if(p->prev==NULL)//如果没有与p前缀相同的节点,则指向root
                        p->prev=root;
                    Q.push(p);
                }
            }
        }
    }
    
    
    bool Trie_search(Trie root, char* word)
    {
        Trie node = root;
        char *p = word;
        int id;
        while( *p )
        {
            id = *p - 'a';
            while(true)
                {
                    if(node->next[id]!=NULL) //匹配成功
                        {
                            node = node->next[id];
                            if(node->exist)
                                    return true;
                            break;
                        }
                    else node=node->prev;   //类似KMP的失配过程,在当前字符匹配失败时,跳转到具有最长公共前后缀的字符继续匹配
                    if(node==root||node==NULL){
                        node=root;
                        break;
                    }
                }
            p++;
        }
        return false;
    }
    
     char str[1000005] ;
    int main()
    {
        Trie root = createTrieNode();     // 初始化字典树的根节点
        bool flag = false;
        int n ;
        scanf ("%d", &n);
        for( int i =0 ; i < n ; i++)
            {
                scanf ("%s", str);
                Trie_insert(root , str);
            }
        Trie_build(root);
        scanf ("%s", str);
        if(Trie_search(root , str))  printf("YES
    ");
        else printf("NO
    ");
        return 0;
    }
  • 相关阅读:
    402STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)微信小程序扫码绑定Air302并通过阿里云物联网平台实现远程通信控制
    002CH32V307(WCH单片机)学习开发CH32V307VCT6开发板硬件使用说明,下载和运行第一个程序
    200CH582M学习开发蓝牙键盘例程测试
    401STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)Android使用APUConfig配网绑定ESP8266并通过阿里云物联网平台实现远程通信控制
    840Air724UG模块(4G全网通GPRS开发)摄像头默认设置是8W像素, 需要把像素调整到30W看此篇
    003CH32V307(WCH单片机)学习开发官方资料学习说明,开发板串口, USB, 网口(LAN8720, 自带PHY), SDIO(SD卡)通信测试
    401STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)微信小程序使用APUConfig配网绑定ESP8266并通过阿里云物联网平台实现远程通信控制
    402STM32+ESP8266+Air302基本控制篇(阿里云物联网平台)APP扫码绑定Air302并通过阿里云物联网平台实现远程通信控制
    Golang中常用的代码优化点
    golang程序优化方法
  • 原文地址:https://www.cnblogs.com/smartweed/p/5863010.html
Copyright © 2020-2023  润新知