刷的一套字典树的题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=120748#overview

  个人喜欢指针的字典树写法,但是大力喜欢数组的写法,反正是一个队的,互补一下反而更好- 。-本来前几题我的指针写法都是用new的,后来发现用new可能会超时,所以不如先静态的分配好足够的内存。因此,这样就需要和数组写法一样算节点数目了。引用一下铭神的原话:“节点数在最坏情况下可以认为是字符数量”,也就是说,节点大小应开单词数量乘以每个单词的长度上限。

  A题,赤裸裸的字典树,直接给代码,但是不好的是,这题用的是new的方法创建新节点,要作模板的话不妨使用后面几题的代码。代码如下:

 #include <stdio.h>
#include <algorithm>
#include <set>
#include <math.h>
#include <vector>
#include <stack>
#include <map>
#include <string.h>
#define t_mid (l+r>>1)
#define ls (o<<1)
#define rs (o<<1 | 1)
#define lson ls,l,t_mid
#define rson rs,t_mid+1,r
using namespace std;
typedef long long ll; struct node
{
int cnt;
node *child[];
node()
{
cnt=;
for(int i=;i<;i++) child[i]=NULL;
}
}; node *root = new node(); void Insert(char *s)
{
node *p = root;
int len = strlen(s);
for(int i=;i<len;i++)
{
int m = s[i] - 'a';
if(p->child[m] != NULL)
{
p = p->child[m];
p->cnt++;
}
else
{
node *newnode = new node();
p->child[m] = newnode;
p = newnode;
p->cnt++;
}
}
} int Search(char *s)
{
int len = strlen(s);
node *p = root;
for(int i=;i<len;i++)
{
int m = s[i] - 'a';
if(p->child[m] == NULL)
{
return ;
}
else p = p->child[m];
}
return p->cnt;
} int main()
{
char s[];
while(gets(s) != NULL && strcmp(s,""))
{
Insert(s);
}
while(gets(s) != NULL)
{
printf("%d\n",Search(s));
}
return ;
}

  B题,把数字化成二进制,然后从高位到低位进行储存,然后匹配的时候,因为是异或,所以用相反的数字匹配就行(1的话匹配字典树里面0的路径,反之亦然)。

这题的代码一开始不知道怎么下手,还是借鉴了别人的代码的= =。代码如下:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll; const int N = ;
const int M = N * 1e5;
int n,m,p,root;
ll a[N]; struct Node
{
int l,r;
int val;
void clear()
{
l=r=-;
}
}node[M]; void Insert(int &root,int deep,ll x)
{
if(root == -)
{
root = p++;
node[root].clear();
} if(deep == -) {node[root].val = x;return;} if(x & a[deep]) Insert(node[root].r,deep-,x); //如果这个数的当前位是1,则向右边走
else Insert(node[root].l,deep-,x);
} void Query(int root,int deep,ll x)
{
if(deep == -)
{
printf("%d\n",node[root].val);
return;
} if((x & a[deep]) && node[root].l != - || node[root].r == -)
{
Query(node[root].l,deep-,x);
}
else
{
Query(node[root].r,deep-,x);
}
} int main()
{
int T,cnt=;
scanf("%d",&T);
a[]=;
for(int i=;i<N;i++) a[i]=a[i-]<<; while(T--)
{
printf("Case #%d:\n",cnt++);
root = -,p=;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
{
int t;
scanf("%d",&t);
Insert(root,N,(ll)t);
} while(m--)
{
int t;
scanf("%d",&t);
Query(,N,(ll)t);
}
}
}

  C题,和B题很像,题意是任意从字典树中取出两个元素,求它们的和和剩下的元素中任意一个进行异或,得到的最大的答案是多少。一开始看了一个人的博客,那个人写的很烦,以为是难题,但是后来参考了另外一个人的代码,发现就是B题。思路很简单,枚举取出的元素,然后将这两个元素从字典树中删除!然后进行匹配,匹配完以后加回去即可。这样,删除操作其实就是Insert操作中,把cnt+1改成-1即可,所以可以把Insert改成update就可以实现两个功能了,具体见代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll; const int N = ;
int num[+]; struct node
{
int val;
int cnt;
node *child[];
node()
{
cnt=;
val = -;
for(int i = ; i < ; i ++)
{
child[i] = NULL;
}
}
}; node *root; void Freedom(node *p)
{
for(int i= ; i < ; i ++)
{
if(p->child[i] != NULL) Freedom(p->child[i]);
} delete p;
} void update(node *p,int deep,ll x,int op)
{
if(deep == -) {p->val = x;return;} int m = ( (x>>deep) & );
if(p->child[m] == NULL)
{
node *newnode = new node();
p->child[m] = newnode;
} p->child[m]->cnt += op;
update(p->child[m],deep-,x,op);
} int Query(node *p,int deep,ll x)
{
if(deep == -) return (int)p->val; int m = - ( (x>>deep) & ) ;
if(p->child[m] != NULL && p->child[m]->cnt != )
{
return Query(p->child[m],deep-,x);
}
else
{
return Query(p->child[-m],deep-,x);
}
} int main()
{
int T,cnt=;
scanf("%d",&T); while(T--)
{
root = new node(); int n;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",num+i);
update(root,N,(ll)num[i],);
} int ans=;
for(int i=;i<=n;i++)
{
for(int j=i+;j<=n;j++)
{
int t1 = num[i] + num[j]; update(root,N,num[i],-); //从字典树里删除这两个数
update(root,N,num[j],-); int t2 = Query(root,N,(ll)t1); //查找最大的值 ans = max(ans,t1^t2); update(root,N,num[i],); //将这两个数再次放回字典树里面
update(root,N,num[j],); }
} printf("%d\n",ans); Freedom(root); //释放内存
}
}

同时,从这题中,还有一个对new出来的节点进行delete的操作,要释放内存。所以以后干脆就不要写new的字典树写法好了。。

  D题,给出一堆的单词,找出每个单词的最短缩写,要求是每个缩写必须能认出这个单词,打个比方,app和apple,那么app作为app的缩写以后,apple的缩写就必须为appl。那么思路就很简单了:对一个单词,不断地匹配,直到一个节点,这个节点的cnt值为1或者这个点已经是这个单词的最后一个字母了,那么到此为止了。可以从上面这两个单词中很好的理解。虽然这题是我独立做出来的,但是查询操作还是写麻烦了,其实不需要用队列来装,只要该节点的cnt值大于1,就输出这个字母即可,直到为1或者单词结束为止。具体见代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
typedef long long ll; int num[+];
char s[+][]; struct node
{
int cnt;
node *child[];
node()
{
cnt=;
for(int i = ; i < ; i ++)
{
child[i] = NULL;
}
}
}; node *root; void Freedom(node *p)
{
for(int i= ; i < ; i ++)
{
if(p->child[i] != NULL) Freedom(p->child[i]);
} delete p;
} void Insert(char *s)
{
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - 'a';
if(p->child[m] == NULL)
{
p->child[m] = new node();
}
p = p->child[m];
p->cnt++;
}
} void Query(char *s)
{
node *p = root;
queue<char> Q;
for(int i=;s[i];i++)
{
int m = s[i] - 'a';
if(p->child[m]->cnt == )
{
while(!Q.empty())
{
char c = Q.front();Q.pop();
putchar(c);
}
printf("%c\n",s[i]);
return;
}
else
{
Q.push(s[i]);
}
p = p->child[m];
} while(!Q.empty())
{
char c = Q.front();Q.pop();
putchar(c);
}
puts("");
} int main()
{
int cnt = ;
root = new node();
while(gets(s[cnt++]) != NULL)
{
Insert(s[cnt-]);
//if(cnt==12) break;
} for(int i=;i<cnt;i++)
{
printf("%s ",s[i]);
Query(s[i]);
}
}

  E题的意思,给出一堆的字符串,如果有一个字符串是另外一个的前缀,那么输出NO,否则输出YES。思路和上题类似,枚举所有字符串,一直到结尾为止,如果结尾那个字母出现的次数大于1,那么这个字符串肯定是另外一个字符串的前缀了。具体见代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
typedef long long ll; int n,tot=;
char s[+][]; struct node
{
int cnt;
node *child[];
void clear()
{
cnt=;
for(int i = ; i < ; i ++)
{
child[i] = NULL;
}
}
}N[+]; node *root; node *newnode()
{
node *p = &N[tot++];
p->clear();
return p;
} void Freedom(node *p)
{
for(int i= ; i < ; i ++)
{
if(p->child[i] != NULL) Freedom(p->child[i]);
} delete p;
} void Insert(char *s)
{
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - '';
if(p->child[m] == NULL)
{
p->child[m] = newnode();
}
p = p->child[m];
p->cnt++;
}
} bool Isok(char *s)
{
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - '';
p = p->child[m];
}
if(p->cnt == ) return true;
else return false;
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
tot=;
root = newnode();
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%s",s[i]);
Insert(s[i]);
} int flag=;
for(int i=;i<=n;i++)
{
if(!Isok(s[i])) {flag=;break;}
}
if(!flag) puts("NO");
else puts("YES");
}
}

  F题,意思是找出字典中的所有串,这个串是由字典中的另外两个串拼成的。第一次做时,n^2拼接然后查找,结果当然是TLE,GG。。然后用了大力的方法:比方说三个单词,abc,qwe,abcqwe。查找abcqwe的时候,到c时,c是某个单词(abc)的结尾并且c的下一个字母并不是abcqwe的结尾,让下一个字母(q)入栈,到时候取出栈内所有元素,然后对每个元素进行继续匹配,如果到最后一个字母时这个字母是另外一个单词的结尾,那么这个单词(abcqwe)就是满足题意的。。这题还是有一点技巧性的,比方说栈内存的是那个字母的位置而不是char。关于判断是不是另外单词的结尾,只要在节点处在设置一个布尔类型的变量ised即可。具体见代码吧:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll; int n,tot=;
char s[+][]; struct node
{
int cnt;
bool ised;
node *child[];
void clear()
{
cnt = ;
ised = false;
for(int i = ; i < ; i ++)
{
child[i] = NULL;
}
}
}N[*(+)]; node *root; node *newnode()
{
node *p = &N[tot++];
p->clear();
return p;
} void Freedom(node *p)
{
for(int i= ; i < ; i ++)
{
if(p->child[i] != NULL) Freedom(p->child[i]);
} delete p;
} void Insert(char *s)
{
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - 'a';
if(p->child[m] == NULL)
{
p->child[m] = newnode();
}
p = p->child[m];
p->cnt++;
}
p->ised = true;
} bool Query(char *s)
{
stack<int> S;
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - 'a';
p = p->child[m];
if(p->ised == true && s[i+]) S.push(i+);
} while(!S.empty())
{
p = root;
int x = S.top();S.pop();
for(int i=x;s[i];i++)
{
int m = s[i] - 'a';
if(p->child[m] == NULL) break;
else p = p->child[m];
if(!s[i+] && p->ised == true) return true;
}
}
return false;
} int main()
{
int cnt = ;
tot = ;
root = newnode();
while(gets(s[cnt++]) != NULL)
{
Insert(s[cnt-]);
//if(cnt == 6) break;
} for(int i=;i<cnt;i++)
{
if(Query(s[i])) puts(s[i]);
}
}

  最后一题也是很吊的一题。题意是模仿手机上的九格输入法,输入了一系列的单词。然后给你一些数字,表示现在的输入,问每次输入一个数字,按照频率,最可能是在输入什么内容。。具体的题意还是看原题吧。然后思路就是同样的匹配,把每一个数字当做一个状态,对这个状态进行若干个方向的选择,选择频率最大的内容即可。具体见代码吧:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <stack>
#include <string>
#include<iostream>
using namespace std;
typedef long long ll; int n,tot=,fre;
string ans;
char str[+];
int cnt[] = {,,,,,,,,,};
char ch[][] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; struct node
{
int cnt;
node *child[];
void clear()
{
cnt = ;
for(int i = ; i < ; i ++)
{
child[i] = NULL;
}
}
}N[+]; node *root; node *newnode()
{
node *p = &N[tot++];
p->clear();
return p;
} void Freedom(node *p)
{
for(int i= ; i < ; i ++)
{
if(p->child[i] != NULL) Freedom(p->child[i]);
} delete p;
} void Insert(char *s,int t)
{
node *p = root;
for(int i=;s[i];i++)
{
int m = s[i] - 'a';
if(p->child[m] == NULL)
{
p->child[m] = newnode();
}
p = p->child[m];
p->cnt += t;
}
} void Query(int st,int ed,node *p,string s)
{
if(st == ed)
{
if(p->cnt > fre)
{
fre = p->cnt;
ans = s;
}
} int num = str[st] - '';
for(int i=;i<cnt[num];i++)
{
//对这个键的各个方向进行试探性的选择
int m = ch[num][i] - 'a';
if(p->child[m] != NULL) Query(st+,ed,p->child[m],s+ch[num][i]);
}
} int main()
{
int T;
scanf("%d",&T);
for(int kase=;kase<=T;kase++)
{
tot = ;
root = newnode();
printf("Scenario #%d:\n",kase);
int n;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
char s[+];
int t;
scanf("%s%d",s,&t);
Insert(s,t);
} scanf("%d",&n);
while(n--)
{
scanf("%s",str);
int len = strlen(str);
for(int i=;i<len;i++)
{
fre = ;
Query(,i,root,"");
if(fre) cout << ans << endl;
else puts("MANUALLY");
}
puts("");
}
puts("");
}
}

  那么,字典树这个专题大概是完了,依稀记得上次百度之星有一道是字典树的没做,lyf给了一个很好的板子,下次有空了再补上。= =!

ACM之路(15)—— 字典树入门练习的更多相关文章

  1. HDU 1251 统计难题(字典树入门模板题 很重要)

    统计难题 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)Total Submi ...

  2. hdu 1251 统计难题 (字典树入门题)

    /******************************************************* 题目: 统计难题 (hdu 1251) 链接: http://acm.hdu.edu. ...

  3. HDU 4825 Xor Sum(01字典树入门题)

    http://acm.hdu.edu.cn/showproblem.php?pid=4825 题意: 给出一些数,然后给出多个询问,每个询问要从之前给出的数中选择异或起来后值最大的数. 思路:将给出的 ...

  4. HDU 5687 字典树入门

    Problem C Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total ...

  5. hdu 1247 (字典树入门)

    Hat’s Words Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  6. hdu2222Keywords Search字典树入门……

    #include<iostream> #include<cstring> using namespace std; struct node { int num; node *n ...

  7. [ACM] hdu 1251 统计难题 (字典树)

    统计难题 Problem Description Ignatius近期遇到一个难题,老师交给他非常多单词(仅仅有小写字母组成,不会有反复的单词出现),如今老师要他统计出以某个字符串为前缀的单词数量(单 ...

  8. HDU 1251 统计难题(字典树模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=1251 题意:给出一些单词,然后有多次询问,每次输出以该单词为前缀的单词的数量. 思路: 字典树入门题. #inc ...

  9. 数据结构~trie树(字典树)

    1.概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 我理解字典树是看了这位大佬博客.还不了解字典树的 ...

随机推荐

  1. 阿里云Centos7 配置二级域名

    之前在自己的服务器上安装了laravel,现在给它个二级域名!结果发现了个小坑= =.不说了,上步骤 首先你要有个自己的域名,可以在万网上买一个,我的还是蛮便宜的... 进入你的阿里云管理台 选择云解 ...

  2. volatile 关键字(修饰变量)

    目录 volatile 关键字(修饰变量) 1. 含义 2. 作用 3. 如何保证可见性 4. 如何禁止指令重排序优化 5. volatile 是不安全的 6. volatile 不适用场景 vola ...

  3. .net core 根据数据库生成实体类

    微软最近几年在跨平台上不断发力,很多.net程序员也摩拳擦掌,对微软寄以厚望.就在最近,微软还推出了asp .net core2.0预览版. 通过对.net core的简单尝试,我发现以往我们开发MV ...

  4. 使用python+selenium获得b站今日播放的动漫

    from selenium import webdriver browser=webdriver.Chrome() browser.get('https://www.bilibili.com/anim ...

  5. docker第二篇 Docker基础用法

    Docker中的容器 lxc -> libcontainer -> runC OCI (Open Container Initiative) 由Linux基金会主导于2015年6月创立 作 ...

  6. interrupt分析

    转载自 https://blog.csdn.net/zhangliangzi/article/details/52485319 interrupt简述 interrupt() 方法只是改变中断状态而已 ...

  7. Spring Cloud(八)高可用的分布式配置中心 Spring Cloud Config

    在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件.在Spring Cloud中,有分布式配置中心组件spring cloud config,它支持配 ...

  8. 【python+beautifulsoup4】Python中安装bs4后,pycharm报错ModuleNotFoundError: No module named 'bs4'

    本文主要分享关于在对应python版本中安装beautifulsoup之后,在代码执行时还会提示“No module named 'bs4'”的问题. 安装beautifsoup4 在命令窗口执行 p ...

  9. MYSQL 遇见各种有意思题库

    1 使用sql查询每个学生a_id最常借图书类型u_id.表名:t1 (学生图书借阅) [问题分析,1 先选出每个学生,每个类型所借数量] SELECT a_id,u_id,count(u_id) a ...

  10. Django—ModelForm

    简介 Model + Form ==> ModelForm.model和form的结合体,所以有以下功能: 验证 数据库操作 Form回顾 models.py class UserType(mo ...