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\n",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\n");
    else printf("NO\n");
    return 0;
}
时间: 2025-01-16 13:04:00

hiho一下 第二周&第四周:从Trie树到Trie图的相关文章

笔试算法题(39):Trie树(Trie Tree or Prefix Tree)

出题:TRIE树 (Trie Tree or Prefix Tree): 分析: 又称字典树或者前缀树,一种用于快速检索的多叉树结构:英文字母的Trie树为26叉树,数字的Trie树为10叉树:All the descendants of a node have a common prefix of the sequence associated with that node, and the root is associated with the empty sequence. 由于不同的se

hihoCoder 1014 Trie树 (Trie)

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 小Hi和小Ho是一对好朋友.出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天.他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:"小Ho.你能不能对于每个我给出的字符串,都在这个词典里面找到以这个字符串开头的全部单词呢?" 身经百战的小Ho答道:"怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的全

hiho一下第二周 Trie树

题目链接:http://hihocoder.com/problemset/problem/1014 1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn = 1e6 + 5; // 1e5 * 10 (1e5个单词 * 单词长度 (<= 10)) 8 con

hihoCoder hiho一下 第二周 #1014 : Trie树(Trie树基本应用)

思路:完全看题目中的介绍就行了.还有里面的input写道:不保证是英文单词,也有可能是火星文单词哦.比赛结束后的提交是不用考虑26个字母之外的,都会AC,如果考虑128种可能的话,爆了内存.步骤就是,在插单词的同时记录该结点之后的单词数,查词就查最后一个字母所在结点上的单词数. 1 #include <iostream> 2 #include <cstring> 3 #include <vector> 4 #include <stdio.h> 5 using

hihocoder_1014: Trie树(Trie树模板题)

题目链接 #include<bits/stdc++.h> using namespace std; const int L=12; struct T { int num; T* next[26]; T() { num=0; int i; for(int i=0;i<26;i++) next[i]=NULL; } }t; void insert(char str[]) { T* p=&t; for(int i=0;str[i];i++) { int a=str[i]-'a'; if

【hdoj】1251 统计难题 【数据结构-Trie树裸题】

传送门:统计难题 题意: 字典树裸题. 分析 字典树板子,但是这题需要注意一点. 关于字典树的只是可以参考hihocoder hiho一下 第二周 用G++提交会爆内存(Memory Limit Exceeded),用c++提交可以AC. G++ 与 C++提交的区别 参考:OJ中的语言选项里G++ 与 C++的区别 C++是一门计算机编程语言,而G++则是C++的编译器. 选择C++意味着你将使用C++最标准的编译方式,也就是ANSI C++编译. 选择G++则意味这你使用GNU项目中适用人群

Trie树标准模版

这是一个Trie树标准模版 By Leo 本人版权,请勿抄袭!! 先看教程:  1. 什么是trie树 1.Trie树 (特例结构树) Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高.      Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率

树结构—Trie树

很有段时间没写此系列了,今天我们来说Trie树,Trie树的名字有很多,比如字典树,前缀树等等. 一:概念 下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢? 从上面的图中,我们或多或少的可以发现一些好玩的特性. 第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符. 第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串. 第三:每个单词的公共前缀作为一个字符节点保存. 二:使用范围 既然学Trie树,我们肯定要知道这玩意是用来干嘛的

6天通吃树结构—— 第五天 Trie树

原文:6天通吃树结构-- 第五天 Trie树 很有段时间没写此系列了,今天我们来说Trie树,Trie树的名字有很多,比如字典树,前缀树等等. 一:概念 下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢? 从上面的图中,我们或多或少的可以发现一些好玩的特性. 第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符. 第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串. 第三:每个单词的公共前缀作为一个字符节点保存. 二:使用范围 既然