刷的一套字典树的题目链接: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. hdu 5446 lucas+crt+按位乘

    http://acm.hdu.edu.cn/showproblem.php?pid=5446 题意:题目意思很简单,要你求C(n,m)mod p的值 p=p1*p2*...pn; 题解:对于C(n,m ...

  2. _stscanf_s (sscanf)正则表达式

    _stscanf_s (sscanf)正则表达式     {%[*] [width] [{h | l | I64 | L}]type  |  ' ' |  '\t' | '\n' | 非%符号}, 注 ...

  3. spark application调度机制(spreadOutApps,oneExecutorPerWorker 算法)

    1.要想明白spark application调度机制,需要回答一下几个问题: 1.谁来调度? 2.为谁调度? 3.调度什么? 3.何时调度? 4.调度算法 前四个问题可以用如下一句话里来回答:每当集 ...

  4. 移植 Linux 内核

    目录 更新记录 1.Linux 版本及特点 2.打补丁.编译.烧写.启动内核 3.内核源码文件结构 4.内核架构分析 4.1 内核配置 4.2 Makefile架构分析 4.3 Kconfig 架构文 ...

  5. # 机器学习算法总结-第四天(SKlearn/数据处理and特征工程)

    总结: 量纲化(归一化,标准化) 缺失值处理(补0.均值.中值.众数.自定义) 编码/哑变量:忽略数字中自带数学性质(文字->数值类型) 连续特征离散化(二值化/分箱处理)

  6. bootsctrap4 datepicker时间选择插件

    现在网上基本都是v3的时间选择插件,花了点时间改了找了个v4能用的 bootstrap-datepicker <!DOCTYPE html> <html> <head&g ...

  7. 使用pycharm 编写代码 并在远程主机上运行

    一 要求 远程主机有python解释器 二 在菜单栏,File -> Settings… -> Project ×× -> Project Interpreter,点击右侧 Add按 ...

  8. Fiddler抓包HTTPS捕捉旧版App

    “现在可以公开的情报:简易操作Fiddler抓包可能” 任何App的更新都限于苹果开发者规定,有时为了上架不得已放弃一些真正实用的功能,比如视频音频的直接下载,脚本的直接导入,手机上IPA的直接安装等 ...

  9. python 前置程序窗口,还原最小化的窗口

    python 前置程序窗口,还原最小化的窗口 在网上找了比较久,大多是: win32gui.FindWindow(class_name, window_name) win32gui.SetForegr ...

  10. python dict list 遍历的几种常见方法

    list 遍历index,value list = ['one', 'two', 'three'] for i in list: print(list.index(i),i) #rangefor i ...