前缀树里面可以存一堆字符串,也可以说是一堆单词,存完之后我们可以轻松判断一个指定的字符串是否出现过

下面我来详细解释一下实现细节

const int maxnode=*+;  //单词个数*每一个单词的字符数
const int sigma_size=;
struct Trie
{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clear()
{
sz=;
memset(ch[],,sizeof(ch[]));
memset(val,,sizeof(val));
}
int idx(char c)
{
return c-''; //这里根据情况修改
}
};

这里面的结点总数为单词个数和每一个单词最多包含的字符数的乘积。

sigma_size是单词字符的字符集大小,数字就是10,字母就是26

看ch之前我们先来理解一下sz,sz是一个自增变量,是每一个结点的唯一标志,也就是ID

ch[i][j]就表示的是第i个结点是第j种字符表示的(很显然sigma_size作为字符集大小),其值为当前结点在创建时分配的ID,也就是sz

idx这个函数是将字符转化成对应的下标的,也就是前面提到的那个j,第j种字符

每一个结点都可以有一个附加信息,附加什么都可以,但是如果这个结点有意义不要附加0,因为附加0的话表示当前结点没意义(*^▽^*)

那我附加什么好呢,我可以附加这个单词在单词数组中的下标,这样我就能直接根据这个附加信息查到这个单词是啥了不用遍历了

接下来我们介绍插入部分

    void insert(const char *s,int v)  //v是每一个单词的附加信息,一定不能是0
{
int u=;
int n=strlen(s);
for(int i=;i<n;i++)
{
int c=idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz],,sizeof(ch[sz]));
val[sz]=;
ch[u][c]=sz++;
}
//这里可以加else改为int函数,注意一定要等输入完再break否则运行错
/*
else
{
if(val[ch[u][c]])
return 0;
if(i==n-1)
return 0;
}
*/
u=ch[u][c];
}
val[u]=v;
}

插入部分的核心就是根据字符串创建ch并分配sz,到底了之后,给这个结点val赋值表示单词结束。

注释部分可以直接把插入改成有返回值的函数,如果之前这个词插过了就不插入了返回0,这里分两种情况,一种是插入的本身就是一个前缀,一种是之前的单词是这次新插入单词的前缀。

void find_prefixes(const char *s,int len,vector<int>& ans)
{
int u=;
for(int i=;i<len;i++)
{
if(s[i]=='\0')
break;
int c=idx(s[i]);
if(!ch[u][c])
break;
u=ch[u][c];
if(val[u]!=)
ans.push_back(val[u]);
}
}

这个函数会在整个树中找当前前缀的所有单词并把他们的标记存在ans数组里,比如前缀是ai,那么aie,air,aiop等的标记就都找到了,相当于这些单词都找到了。

完整代码如下,实现了一个动态的插入和判断当前插入的单词是否是已经插入的单词前缀的过程(两种情况哦)。

 #include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int maxnode=*+; //单词个数*每一个单词的字符数
const int sigma_size=;
struct Trie
{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
void clear()
{
sz=;
memset(ch[],,sizeof(ch[]));
memset(val,,sizeof(val));
}
int idx(char c)
{
return c-''; //这里根据情况修改
}
void insert(const char *s,int v) //v是每一个单词的附加信息,一定不能是0
{
int u=;
int n=strlen(s);
for(int i=;i<n;i++)
{
int c=idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz],,sizeof(ch[sz]));
val[sz]=;
ch[u][c]=sz++;
}
//这里可以加else改为int函数,注意一定要等输入完再break否则运行错
/*
else
{
if(val[ch[u][c]])
return 0;
if(i==n-1)
return 0;
}
*/
u=ch[u][c];
}
val[u]=v;
}
void find_prefixes(const char *s,int len,vector<int>& ans)
{
int u=;
for(int i=;i<len;i++)
{
if(s[i]=='\0')
break;
int c=idx(s[i]);
if(!ch[u][c])
break;
u=ch[u][c];
if(val[u]!=)
ans.push_back(val[u]);
}
}
};
const int maxn=;
int n;
Trie trie;
char word[maxn][];
int main()
{
int T;
cin>>T;
while(T--)
{
trie.clear();
int flag=;
cin>>n;
for(int i=;i<n;i++)
{
cin>>word[i];
trie.insert(word[i],i+);
}
vector<int> p;
for(int i=;i<n;i++)
{
p.clear();
trie.find_prefixes(word[i],strlen(word[i])-,p);
if(p.size()!=)
{
flag=;
break;
}
}
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return ;
}
ch[maxnode][sigma_size]

数据结构&字符串:字典树的更多相关文章

  1. 算法与数据结构基础 - 字典树(Trie)

    Trie基础 Trie字典树又叫前缀树(prefix tree),用以较快速地进行单词或前缀查询,Trie节点结构如下: //208. Implement Trie (Prefix Tree)clas ...

  2. 数据结构 -- Trie字典树

    简介 字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 性质:   1.  根节 ...

  3. 字符串hash与字典树

    title: 字符串hash与字典树 date: 2018-08-01 22:05:29 tags: acm 算法 字符串 概述 这篇主要是关于字符串里的 字符串hash 和 字符串字典树,,两个都是 ...

  4. python3实现互信息和左右熵的新词发现--基于字典树

    字典树 原来讲明白了剩下的就是具体实现了,最适合存储和计算词频的数据结构就是字典树,这里给一个讲解的很清楚的链接 具体代码 代码已开源,需要的点击这个Github

  5. 数据结构&字符串:01字典树

    利用01字典树查询最大异或值 01字典树的是只含有0和1两种字符的字典树,在使用它的时候,把若干数字转成二进制后插入其中 在查询树中的哪个数字和给定数字有最大异或值的时候,从根开始贪心查询就ok了 H ...

  6. [数据结构]字典树(Tire树)

    概述: Trie是个简单但实用的数据结构,是一种树形结构,是一种哈希树的变种,相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串.和普通树不同的地方是,相同的字符 ...

  7. Trie树|字典树(字符串排序)

    有时,我们会碰到对字符串的排序,若采用一些经典的排序算法,则时间复杂度一般为O(n*lgn),但若采用Trie树,则时间复杂度仅为O(n). Trie树又名字典树,从字面意思即可理解,这种树的结构像英 ...

  8. NYOJ 163 Phone List (字符串处理 字典树)

    题目链接 描述 Given a list of phone numbers, determine if it is consistent in the sense that no number is ...

  9. 【字符串算法】字典树(Trie树)

    什么是字典树 基本概念 字典树,又称为单词查找树或Tire树,是一种树形结构,它是一种哈希树的变种,用于存储字符串及其相关信息. 基本性质 1.根节点不包含字符,除根节点外的每一个子节点都包含一个字符 ...

  10. I: Carryon的字符串排序(字典树/map映射)

    2297: Carryon的字符串 Time Limit: C/C++ 1 s      Java/Python 3 s      Memory Limit: 128 MB      Accepted ...

随机推荐

  1. 如何遍历一个文件夹(C语言实现)

    #include<io.h> #include<stdio.h> int main() { long Handle; struct _finddata_t FileInfo; ...

  2. postmortem report of period M2

    一.设想和目标 1.我们的软件主要要解决学长设计的学霸系统中视频及文档的浏览功能问题. 2.时间相对充裕.不过对于我们这些零基础的人来说还是比较困难. 3.我们团队中不同意见通常会进行进一步讨论,说出 ...

  3. 软件工程课堂作业(一)——随机产生四则运算题目(C++)

    一.设计思想: 1.首先主函数只用来调用随机产生并输出运算题目函数,随机产生并输出这一部分功能用一个randout函数实现: 2.随机产生运算数这一功能,两个运算数可以用随机函数生成,并将它们控制在1 ...

  4. 福大软工1816:Alpha(8/10)

    Alpha 冲刺 (8/10) 队名:第三视角 组长博客链接 本次作业链接 团队部分 团队燃尽图 工作情况汇报 张扬(组长) 过去两天完成了哪些任务: 文字/口头描述: 1. 2. 展示GitHub当 ...

  5. 团队作业7——第二次项目冲刺(Beta版本)-第一篇

    1.当天站立式会议照片: 2.工作分工: 团队成员 分工 郭达22120 项目整合,后台代码 刘德培44060 数据库模块后台连接 石浩洋22061 前台界面优化 曾繁钦22056 前台界面优化.测试 ...

  6. 关于PHP使用GD库生成的验证码无法在别处显示

    https://segmentfault.com/q/1010000002522270

  7. iOS音频播放概述

    在iOS系统中apple对音频播放需要的操作进行了封装并提供了不同层次的接口 下面对其中的中高层接口进行功能说明: Audio File Services:读写音频数据,可以完成播放流程中的第2步: ...

  8. IDEA编译的JAR包运行出现“没有主清单属性”

    运行编译好的包出现: 解决方案就是: 确保MANIFEST.MF文件在src/main/resources/META_INF/而不是src/main/java/META_INF/

  9. 【Quartz.net】- Cron表达式

    一.结构 corn从左到右(用空格隔开):秒 分 小时 月份中的日期 月份 星期中的日期 年份 二.各字段的含义   字段 允许值 允许的特殊字符 秒(Seconds) 0~59的整数 , - * / ...

  10. 【Python】python学习之总结

    迭代器: def gen(): a = yield a a = a * yield a for i in gen(): print(i) 创建一个函数,循环体,yield循环到此就返回一个值.调用函数 ...