Trie图的学习过程
学习字典树有段日子了!上次去做福州赛区2010年的试题的时候,里面有一道题:字符串的多串匹配!当时就觉得应该是字典树的题,但是分析了一下后数据量太大了!就没做它!下来翻书一看,果然跟字典树有关,但不是字典树!如果用字典树肯定会超时,而不是超空间!
不管怎么样,还是基础的数据结构和算法都没有学全,没有进行系统的学习啊!很多时候感叹,如果有老师能指点一下,如果有志同道合的朋友一起学习算法,也许结果就不是现在这个样子了!无论怎么样,坚持着往前走吧!
字典树就很简单了,网上有很多很多的说明和代码,其思想也很容易理解:就是把字符串集合中前缀相同的部分用同一个结点表示! 回想起前两天没有研究透彻的KMP算法,以及后缀数组。发现字符串的问题,不是用到了前缀,就是用到了后缀!
贴一张原理图,然后给出我个人觉得相当优雅的实现代码吧!
- /*
- http://poj.org/problem?id=3630
- POJ 3630 Phone List
- 大意:电话簿中有n个电话号码,判断这些号码是否合法。
- 若某个电话号码是另一个电话号码的前缀,则该号码簿非法
- 分析:字典树即可
- 注意点,字典树在插入过程中新建节点会超时,故节点用数组的方式存储
- */
- #include<stdio.h>
- #include<string.h>
- #define N 10
- #define M 100000
- struct TrieNode{
- TrieNode *next[N];//由于这里面实际上已经包含了data,所以data是不需要的
- bool isTail;
- TrieNode(){
- isTail=false;
- memset(next,NULL,sizeof(next));
- }
- };
- TrieNode node[M];//这个里面存储了所有的结点
- class Trie{
- public:
- Trie();
- bool insert(char *word);
- void clear();
- private:
- TrieNode *root;
- int nodeNum;//树中实际出现的节点数
- };
- Trie::Trie(){
- root=&node[0];
- nodeNum = 1;
- }
- bool Trie::insert(char *word){
- TrieNode *p=root;
- while(*word){
- int current=*word-'0';
- if(p->next[current]==NULL){
- p->next[current]=&node[nodeNum++];
- }
- p=p->next[current];
- if(p->isTail==true){
- return true;
- }
- word++;
- }
- p->isTail=true;
- //如果它的后面还有结点,则表明此串必为一串的子串
- for(int i=0;i<N;i++){
- if(p->next[i]!=NULL){
- return true;
- }
- }
- return false;
- }
- void Trie::clear(){
- for(int i=0;i<nodeNum;i++){
- memset(node[i].next,NULL,sizeof(node[i].next));
- node[i].isTail=false;
- }
- nodeNum=1;//这一步是容易忘记的
- }
- int main(){
- int T;
- char ch[M];
- scanf("%d",&T);
- Trie trie;
- while(T-->0){
- int n;
- scanf("%d",&n);
- trie.clear();
- bool isIll=false;
- while(n-->0){
- scanf("%s",ch);
- if(isIll==true)
- continue;
- isIll=trie.insert(ch);
- }
- if(isIll){
- printf("NO\n");
- }else{
- printf("YES\n");
- }
- }
- return 0;
- }
字典树就介绍完了!可是遇到新问题的时候,比如:HDU2222,虽然题目是英文的,挺容易看懂!字典树就用不上了!那需要用什么呢?其实也有很多算法,我也没学习过,比如:AC自动机,WM算法等等!主要是KMP都没过关,那些算法也学习不了啊!但是Trie树学习过,而Trie图是可以解决这类问题的!果断学习一下Trie图吧!
对于hdu2222,先把超时的代码贴上来吧!记得马云好像说过,多去学习失败的经验!
- /*
- * Trie图:解决多串匹配问题
- * hdu2222
- * */
- #include<stdio.h>
- #include<string.h>
- #define M 1000005
- #define N 100000
- char word[60];
- char s[M];
- struct Node{
- bool tail;
- Node *next[26];
- Node(){
- memset(next,0,sizeof(next));
- tail=false;
- }
- }tree[N],*root;
- int nodeNum;
- void init(){
- root=&tree[0];
- nodeNum=1;
- }
- void insert(char *word){
- Node *p=root;
- int idx;
- while(*word){
- idx=*word-'a';
- if(p->next[idx]==0){
- p->next[idx]=&tree[nodeNum++];
- }
- p=p->next[idx];
- word++;
- }
- p->tail=true;
- }
- void clear(){
- int i;
- for(i=1;i<nodeNum;i++){
- memset(tree[i].next,0,sizeof(tree[i].next));
- tree[i].tail=false;
- }
- nodeNum=1;
- }
- int search(char *s){
- int ans=0;
- Node *p;
- int i,idx,cur,len;
- len=strlen(s);
- for(i=0;i<len;i++){
- idx=i;p=root;
- while(1){
- cur=s[idx]-'a';
- if(p->tail==true)ans++;
- if(p->next[cur]==0)break;
- p=p->next[cur];idx++;
- }
- }
- return ans;
- }
- int main(){
- int t,n,i;
- scanf("%d",&t);
- init();
- while(t--){
- clear();
- scanf("%d",&n);
- for(i=0;i<n;i++){
- scanf("%s",word);
- insert(word);
- }
- scanf("%s",s);
- printf("%d\n",search(s));
- }
- }
好了,现在来看Trie图是怎么解决吧!Trie图很好懂,就是在Trie树的基础上把每个结点上增加一个前缀指针!至于Trie图的具体细节后面再学习,先把AC了的代码贴上来吧!
- /*
- * Trie图:解决多串匹配问题
- * hdu2222
- * */
- #include<stdio.h>
- #include<string.h>
- #include<queue>
- using namespace std;
- #define M 1000005
- char word[60];
- char s[M];
- struct Node{
- int tail;
- Node *prefix;
- Node *next[26];
- Node(){
- memset(next,0,sizeof(next));
- tail=0;
- }
- }*root;
- void init(){
- root=new Node();
- }
- void insert(char *word){
- Node *p=root;
- int idx;
- while(*word){
- idx=*word-'a';
- if(p->next[idx]==0){
- p->next[idx]=new Node();
- }
- p=p->next[idx];
- word++;
- }
- p->tail++;
- }
- void add_prefix(){
- root->prefix = NULL;
- deque<Node* > q;
- q.push_back(root);
- while(!q.empty()) {
- Node* tmp = q.front();
- Node* p = NULL;
- q.pop_front();
- for(int i = 0; i < 26; ++i) {
- if(tmp->next[i] != NULL) {
- if(tmp == root) tmp->next[i]->prefix = root;
- else {
- p = tmp->prefix;
- while(p != NULL) {
- if(p->next[i] != NULL) {
- tmp->next[i]->prefix = p->next[i];
- break;
- }
- p = p->prefix;
- }
- if(p == NULL) tmp->next[i]->prefix = root;
- }
- q.push_back(tmp->next[i]);
- }
- }
- }
- }
- int search(char *st){
- int cnt = 0, t;
- Node* p = root;
- while(*st) {
- t = *st - 'a';
- while(p->next[t] == NULL && p != root) {
- p = p->prefix;
- }
- p = p->next[t];
- if(p == NULL) p = root;
- Node* tmp = p;
- while(tmp != root && tmp->tail != -1) {
- cnt += tmp->tail;
- tmp->tail = -1;
- tmp = tmp->prefix;
- }
- st++;
- }
- return cnt;
- }
- int main(){
- int t,n,i;
- scanf("%d",&t);
- while(t--){
- init();
- scanf("%d",&n);
- for(i=0;i<n;i++){
- scanf("%s",word);
- insert(word);
- }
- add_prefix();
- scanf("%s",s);
- printf("%d\n",search(s));
- }
- }
由于时间的关系,暂时先把Trie图的学习放到一边,把参考博客列出来,供自己以后学习吧
http://baidutech.blog.51cto.com/4114344/743727
http://www.cnblogs.com/vongang/archive/2012/07/24/2606494.html
转载于:https://blog.51cto.com/sbp810050504/1034874
Trie图的学习过程相关推荐
- hiho一下 第四周 Hihocoder #1036 : Trie图
#1036 : Trie图 时间限制:20000ms 单点时限:1000ms 内存限制:512MB 描述 前情回顾 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从 ...
- 【BZOJ-2938】病毒 Trie图 + 拓扑排序
2938: [Poi2000]病毒 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 609 Solved: 318 [Submit][Status][ ...
- hdu2457 Trie图+dp
hdu2457 给定n个模式串, 和一个文本串 问如果修改最少的字符串使得文本串不包含模式串, 输出最少的次数,如果不能修改成功,则输出-1 dp[i][j] 表示长度为i的字符串, 到达状态j(Tr ...
- BZOJ 1444 [JSOI2009]有趣的游戏 (Trie图/AC自动机+矩阵求逆)
题目大意:给你$N$个长度相等且互不相同的模式串,现在有一个字符串生成器会不断生成字符,其中每个字符出现的概率是$p_{i}/q_{i}$,当生成器生成的字符串包含了某个模式串,则拥有该模式串的玩家胜 ...
- HiHocoder 1036 : Trie图 AC自动机
Trie图 先看一个问题:给一个很长很长的母串 长度为n,然后给m个小的模式串.求这m个模式串里边有多少个是母串的字串. 最先想到的是暴力O(n*m*len(m)) len(m)表示这m个模式串的平均 ...
- POJ 1625 Censored ( Trie图 DP 高精度 )
题意 : 给出 n 个单词组成的字符集 以及 p 个非法串,问你用字符集里面的单词构造长度为 m 的单词的方案数有多少种? 分析 : 与 POJ 2778 非常相似的一道题目,如果没有做过就尝试去了解 ...
- 【Trie图】Hiho4_Hihocoder
前情回顾 上回说到,小Hi和小Ho接受到了河蟹先生伟大而光荣的任务:河蟹先生将要给与他们一篇从互联网上收集来的文章,和一本厚厚的河蟹词典,而他们要做的是判断这篇文章中是否存在那些属于河蟹词典中的词语. ...
- HDU 4511 小明系列故事——女友的考验 ( Trie图 DP )
题意 : 给出编号从1 ~ n 的 n 个平面直角坐标系上的点,求从给出的第一个点出发到达最后一个点的最短路径,其中有两种限制,其一就是只能从编号小的点到达编号大的点,再者不能走接下来给出的 m 个 ...
- [hiho 04]Trie图
题目描述 Trie 图就是在 Trie 树上建立 fail 指针,类似于KMP算法中的next数组的作用. 这个数据结构的作用是判断一个字符串中是否包含一组字符串中的任意一个. 结构体定义是这样的: ...
- BZOJ1444: [Jsoi2009]有趣的游戏(Trie图,矩乘)
Description Input 注意 是0<=P, n , l, m≤ 10. Output Sample Input input 1 3 2 2 1 2 1 2 AB BA AA inpu ...
最新文章
- C#机房重构-总结(三)
- python库--tensorflow--RNN(循环神经网络相关)
- 记住密码后input黄色背景处理
- linux下获取系统时间的方法
- 三维重建16:概率图模型 模板类编程
- scriptalert(1)/script
- (106)System Verilog类中变量双向约束关系
- ngrinder安装
- @软考考生,你想要的论文写作方法及规范,给你安排上了!
- n1盒子当无线打印服务器,n1下ubuntu安装cups配置airprint网络打印服务器
- macos虚拟机鼠标不能移动和键盘不能使用
- 【甄选靶场】Vulnhub百个项目渗透——项目十:stapler-1(文件上传,多方式提权)
- 鬼知道我昨晚经历了什么......
- Cocos2d-JS中ctor和Cocos2d-x中init的作用
- 【转】激励循环——加密算法如何实际修复现有激励循环
- 条件生成对抗神经网络,生成对抗网络gan原理
- ubuntu 下安装labelImg报错
- Android稳定性测试工具Monkey的使用
- cocos-lua学习笔记(九)动作
- Android APP必备高级功能,消息推送之MQTT