感觉不把这个Trie理解一下,AC自动机的代码看起来有点费劲。

这里代码的学习仿照训练指南209页。

这里如果只是查询单词,感觉用map更好,但是如果查前缀,还是用Trie。

1、Trie查询前缀字符串是否存在。

 #include <bits/stdc++.h>
using namespace std;
#define mst(s,v) memset(s, v, sizeof(s));
const int maxnode = ;
const int sigma_size = ;
//字母表为全体小写字母的Trie
struct Trie{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz; //结点总数
Trie(){ sz=; mst(ch[], ); mst(val, ); } //初始时只有一个根节点
int idx(char c) { return c-'a'; }
//插入字符串s,附加信息为v。v!=0(0代表本身不是单词结点)
void insert(char *s, int v){
int u = , 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] = ; //中间结点的附加信息为0
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v; //字符串的最后一个字符的附加信息
//标记在末结点,查询整个单词时有用
}
bool find(char *s){
int u = , len = strlen(s);
for(int i=; i<len; i++){
int c = idx(s[i]);
if(!ch[u][c]) return false;
u = ch[u][c];
}
return true; //查询整个单词时 return val[u]
}
}tree;
int main(){
freopen("in.txt", "r", stdin);
int n, m;
cin >> n >> m;
char tmp[];
for(int i=; i<n; i++){
cin >> tmp;
cout << tmp << endl;
tree.insert(tmp, );
}
for(int i=; i<m; i++){
cin >> tmp;
cout << endl << tmp << endl;
if(tree.find(tmp)) cout << "Y" << endl;
else cout << "N" << endl;
}
return ;
}
/*
6 2
shex
shexy
yasherhs
say
shrxy
sher
yasherhs
she
*/

2、Trie查询前缀个数

 #include <bits/stdc++.h>
using namespace std;
#define mst(s,v) memset(s, v, sizeof(s));
const int maxnode = ;
const int sigma_size = ;
//可以查找前缀,也可以查找单词(map)
struct Trie{
int ch[maxnode][sigma_size];
int sum[maxnode];
int sz; //结点总数
Trie(){ sz=; mst(ch[], ); mst(sum, ); }
int idx(char c) { return c-'a'; }
//插入字符串s,附加信息为v。v!=0(0代表本身不是单词结点)
void insert(char *s){
int u = , n = strlen(s);
for(int i=; i<n; i++){
int c = idx(s[i]);
if(!ch[u][c]){ //结点不存在
memset(ch[sz], , sizeof(ch[sz]));
ch[u][c] = sz++;
}
sum[ch[u][c]]++;
u = ch[u][c];
}
}
int find(char *s){
int u = , len = strlen(s);
for(int i=; i<len; i++){
int c = idx(s[i]);
if(!ch[u][c]) return ;
u = ch[u][c];
}
return sum[u];
}
}tree;
int main(){
freopen("in.txt", "r", stdin);
int n, m;
cin >> n >> m;
char tmp[];
for(int i=; i<n; i++){
cin >> tmp;
tree.insert(tmp);
}
for(int i=; i<m; i++){
cin >> tmp;
cout << tmp << endl;
cout << tree.find(tmp) << endl;
}
return ;
}

3、用指针理解Trie

#include <bits/stdc++.h>
using namespace std;
#define mst(s,v) memset(s, v, sizeof(s));
const int sigma_size = ;
struct node{
int count;
node * next[];
}*root;
int idx(char c){ return c-'a'; }
node* build(){
node* k = new(node);
k->count = ;
mst(k->next , );
return k;
}
void insert(char* s){
node* r = root;
char* word = s;
while(*word){
int c = idx(*word);
if(r->next[c] == NULL) r->next[c] = build();
r = r->next[c];
r->count++;
word++;
}
}
int find(char* s){
node* r = root;
char* word = s;
while(*word){
int c = idx(*word);
r = r->next[c];
if(r==NULL) return ;
word++;
}
return r->count;
}
int main(){
freopen("in.txt", "r", stdin);
int n, m;
cin >> n >> m;
char tmp[];
root = build();
for(int i=; i<n; i++){
cin >> tmp;
insert(tmp);
}
for(int i=; i<m; i++){
cin >> tmp;
cout << tmp << endl;
cout << find(tmp) << endl;
}
return ;
}

Trie灵活的地方是在插入时对点的属性的更新,以及查询时与其他算法的结合。

这里将AC自动机summer-work那里的C - L语言拿过来,感受在查询变化的一些思路。

题意:n(1,20)模式串(可以有包含关系(这就决定了不能贪心)),m(1,20)文本串,对每个文本串,n个模式串最多可以组成的最长前缀的位置。

这题就是在find()上思考解法,正解应该是dp[i]是否能理解前i个单词,这里因为是学习Trie,所以就暴力吧(感觉自己太水)。

这里总结一个套路,因为是单词而非前缀,所以insert()时点的属性是val=1。

为了创造vis(是否理解前i个单词)的边界条件vis[0]=v(明显可以识别),所以这里scanf(s+1),即遍历时从i+1开始。

 #include <bits/stdc++.h>
using namespace std;
#define mst(s,v) memset(s, v, sizeof(s));
const int maxnode = ;
const int sigma_size = ;
const int maxlen = 1e6 + 5e4;
int dp[maxlen];
struct Trie{
int ch[maxnode][sigma_size];
int val[maxlen];
int sz;
Trie(){ sz=; }
inline int idx(char c) { return c-'a'; }
inline void insert(char *s){
int u = , n = strlen(s);
for(int i=; i<n; i++){
int c = idx(s[i]);
if(!ch[u][c]){
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = ; //标记单词
}
inline int find(char *s, int v){
int u = , len = strlen(s), ans = ;
dp[] = v; //标记能够被识别的前缀位置
for(int i=; i<len; i++){
if(dp[i] == v){
u = ;
for(int j=i+; j<len; j++){
int c = idx(s[j]);
if(!ch[u][c]) break;
//cout<<"u = "<<u<<" v = "<<v<<" s[j] = "<<s[j]<<endl;
u = ch[u][c];
if(val[u]){ //找到单词的前缀
ans = max(ans, j);
dp[j] = v; //标记位置
}
}
}
}
return ans;
}
}tree;
char tmp[maxlen];
int main(){
// freopen("in.txt", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
for(int i=; i<n; i++){
scanf("%s", tmp);
tree.insert(tmp);
}
for(int i=; i<=m; i++){
scanf("%s", tmp+);
printf("%d\n", tree.find(tmp, i));
}
return ;
}

Trie代码学习的更多相关文章

  1. u-boot代码学习内容

    前言  u-boot代码庞大,不可能全部细读,只能有选择的读部分代码.在读代码之前,根据韦东山教材,关于代码学习内容和深度做以下预先划定. 一.Makefile.mkconfig.config.mk等 ...

  2. Objective-C代码学习大纲(3)

    Objective-C代码学习大纲(3) 2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍 ...

  3. ORB-SLAM2 论文&代码学习 ——Tracking 线程

    本文要点: ORB-SLAM2 Tracking 线程 论文内容介绍 ORB-SLAM2 Tracking 线程 代码结构介绍 写在前面 上一篇文章中我们已经对 ORB-SLAM2 系统有了一个概览性 ...

  4. ORB-SLAM2 论文&代码学习 —— 单目初始化

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12358458.html 本文要点: ORB-SLAM2 单目初始化 ...

  5. ORB-SLAM2 论文&代码学习 —— LocalMapping 线程

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12360913.html 本文要点: ORB-SLAM2 Local ...

  6. Learning Memory-guided Normality代码学习笔记

    Learning Memory-guided Normality代码学习笔记 记忆模块核心 Memory部分的核心在于以下定义Memory类的部分. class Memory(nn.Module): ...

  7. 3.1.5 LTP(Linux Test Project)学习(五)-LTP代码学习

    3.1.5 LTP(Linux Test Project)学习(五)-LTP代码学习 Hello小崔 ​ 华为技术有限公司 Linux内核开发 2 人赞同了该文章 LTP代码学习方法主要介绍两个步骤, ...

  8. Apollo代码学习(七)—MPC与LQR比较

    前言 Apollo中用到了PID.MPC和LQR三种控制器,其中,MPC和LQR控制器在状态方程的形式.状态变量的形式.目标函数的形式等有诸多相似之处,因此结合自己目前了解到的信息,将两者进行一定的比 ...

  9. Dagger2 生成代码学习

    接上一篇文章介绍了Dagger2的初步使用,相信刚接触的人会觉得很奇怪,怎么会有很多自己没有定义的代码出现,为什么Component的创建方式是那样的.为了搞清楚这些东西,我们需要查看一下Dagger ...

随机推荐

  1. java -为什么重写equals(),还需要重写hashCode()?

    1.先post这两个方法的基本定义: equals()的定义: 浅谈Java中的equals和==(转) hashCode()的定义: java中hashCode()方法的作用 Java中hashCo ...

  2. PEP8规范 Python

    前言 从很多地方搬运+总结,以后根据这个标准再将python的一些奇技淫巧结合起来,写出更pythonic的代码~ PEP8 编码规范 英文原版请点击这里 以下是@bobo的整理,原文请见PEP8 P ...

  3. JVM命令jps

      jps是JVM的一个常用命令,类似linux中的ps命令.jps是查看java进程信息的命令:ps是查看linux系统中进程的命令   格式      jps [ options ] [ host ...

  4. php 5.6 与 php 7 的区别

    1. PHP7.0 比PHP5.6性能提升了两倍. 2.PHP7.0全面一致支持64位. 3.PHP7.0之前出现的致命错误,都改成了抛出异常. 4.增加了空结合操作符(??).效果相当于三元运算符. ...

  5. docker修改数据库密码

    运行mysql(--name 容器名称  -e MYSQL_ROOT_PASSWORD设置初始密码  -p 3307:3306  端口映射,主机端口3307) docker run --name my ...

  6. jQuery 查找父节点 parents()与closest()

    parents()由内向外,直到最高的父节点停止查找,返回的父节点是多个 closest()由内向外查找,当找到符合规则的一个,则不再查找,返回的是0或1个

  7. 使用PHP读取PHP文件并输出到屏幕上

    看完这篇文章,你一定忘不掉htmlentities的用法 背景 今天有个需求,就是一个PHP开发的网址中,有一个页面可以提供给用户修改已经存在的PHP文件中的代码,并POST到服务器上保存. 每次将读 ...

  8. DNS服务基础

    DNS服务器的功能 – 正向解析:根据注册的域名查找其对应的IP地址 – 反向解析:根据IP地址查找对应的注册域名(不常用) NS(声明DNS记录) A(正向解析记录) CNAME(解析记录别名) 安 ...

  9. vue 解析时表达式闪烁的问题

    现象: 在使用 vuejs.angularjs 开发时,经常会遇见浏览器页面闪现表达式 ({{ express }} ), 或者是模块(div)的闪烁. 原因: 由于 JavaScript 去操作DO ...

  10. php上传大文件的解决方案

    1.使用PHP的创始人 Rasmus Lerdorf 写的APC扩展模块来实现(http://pecl.php.net/package/apc) APC实现方法: 安装APC,参照官方文档安装,可以使 ...