目录

一,字典树

二,OJ实战

CSU 1115 最短的名字

HDU 1075 What Are You Talking About

力扣 1804. 实现 Trie (前缀树) II

力扣 139. 单词拆分

力扣 140. 单词拆分 II


一,字典树

字典树,又称前缀树。

前缀树的3个基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。

如图,没画出来的代表空指针。

节点11对应的字符串金山aba,节点15对应的字符串就是caaa

应用场景:存储大量字符串并频繁查找

template<typename T>
class Trie
{
public:vector<vector<int>>v;map<int, int>isEnd;//以任一节点作为尾节点的数目(不一定是叶子节点)map<int, int>deep;//任一节点的深度Trie(){auto valueNum = ValueNum(T{});v.push_back(vector<int>(valueNum + 1, 0));}void push(const T* s, int len, int value = 0){auto minValue = MinValue(T{});auto valueNum = ValueNum(T{});int j = 0;for (int i = 0; i < len; i++){if (v[j][s[i] - minValue + 1] == 0){v[j][s[i] - minValue + 1] = v.size();v.push_back(vector<int>(valueNum + 1, 0));deep[v.size()-1] = i + 1;}j = v[j][s[i] - minValue + 1];}v[j][0] = value;isEnd[j]++;}int find(const T* s, int len, int &maxDeep, vector<int>&ends)//deep是搜到的最大长度,ends是路过哪些end节点{auto minValue = MinValue(T{});int j = 0;maxDeep = 0;for (int i = 0; i < len; i++){if (v[j][s[i] - minValue + 1] == 0)return 0;j = v[j][s[i] - minValue + 1];maxDeep++;if (isEnd[j])ends.push_back(j);}return v[j][0];}int find(const T* s, int len){int maxDeep;vector<int>ends;return find(s, len, maxDeep, ends);}
private:static char MinValue(char){return 'a';}static int MinValue(int){return 0;}static int ValueNum(char) {return 26;}static int ValueNum(int) {return 10;}
};

二,OJ实战

CSU 1115 最短的名字

题目:

Description

在一个奇怪的村子中,很多人的名字都很长,比如aaaaa, bbb and abababab。

名字这么长,叫全名显然起来很不方便。所以村民之间一般只叫名字的前缀。比如叫'aaaaa'的时候可以只叫'aaa',因为没有第二个人名字的前三个字母是'aaa'。不过你不能叫'a',因为有两个人的名字都以'a'开头。村里的人都很聪明,他们总是用最短的称呼叫人。输入保证村里不会有一个人的名字是另外一个人名字的前缀(作为推论,任意两个人的名字都不会相同)。

如果村里的某个人要叫所有人的名字(包括他自己),他一共会说多少个字母?

Input

输入第一行为数据组数T (T<=10)。每组数据第一行为一个整数n(1<=n<=1000),即村里的人数。以下n行每行为一个人的名字(仅有小写字母组成)。输入保证一个村里所有人名字的长度之和不超过1,000,000。

Output

对于每组数据,输出所有人名字的字母总数。

Sample Input

1
3
aaaaa
bbb
abababab

Sample Output

5

思路:字典树

ans[i][j]表示第i个节点的第j个孩子的编号,j>0

ans[i][0]表示第i个节点的所有后代中叶子节点的个数

代码:

#include<iostream>
#include<string>
using namespace std;const int m = 500000;
int ans[m][27], key = 0;int f(int k)
{if (ans[k][0] == 1)return 1;int r = ans[k][0];for (int i = 1; i <= 26; i++)if (ans[k][i])r += f(ans[k][i]);return r;
}int main()
{int T,n;string s;cin >> T;while (T--){cin >> n;memset(ans[0], 0, sizeof(ans[0]));while (n--){cin >> s;for (int i = 0,j=0; s[i]; i++){if (ans[j][s[i] - 'a' + 1] == 0){ans[j][s[i] - 'a' + 1] = ++key;memset(ans[key], 0, sizeof(ans[key]));}ans[j][0]++;j = ans[j][s[i] - 'a' + 1];}}cout << f(0) - ans[0][0] << endl;}return 0;
}

HDU 1075 What Are You Talking About

Ignatius is so lucky that he met a Martian yesterday. But he didn't know the language the Martians use. The Martian gives him a history book of Mars and a dictionary when it leaves. Now Ignatius want to translate the history book into English. Can you help him?

Input

The problem has only one test case, the test case consists of two parts, the dictionary part and the book part. The dictionary part starts with a single line contains a string "START", this string should be ignored, then some lines follow, each line contains two strings, the first one is a word in English, the second one is the corresponding word in Martian's language. A line with a single string "END" indicates the end of the directory part, and this string should be ignored. The book part starts with a single line contains a string "START", this string should be ignored, then an article written in Martian's language. You should translate the article into English with the dictionary. If you find the word in the dictionary you should translate it and write the new word into your translation, if you can't find the word in the dictionary you do not have to translate it, and just copy the old word to your translation. Space(' '), tab('\t'), enter('\n') and all the punctuation should not be translated. A line with a single string "END" indicates the end of the book part, and that's also the end of the input. All the words are in the lowercase, and each word will contain at most 10 characters, and each line will contain at most 3000 characters.

Output

In this problem, you have to output the translation of the history book.

Sample Input

START
from fiwo
hello difh
mars riwosf
earth fnnvk
like fiiwj
END
START
difh, i'm fiwo riwosf.
i fiiwj fnnvk!
END

Sample Output

hello, i'm from mars.
i like earth!

AC代码一(Map实现):

#include <iostream>
#include<string>
#include<map>
using namespace std;int main()
{string a, b;char *ch = new char[3005], *p;map<string, string>m; m.clear();cin >> a;while (cin >> a >> b){if (a[0] == 'E')break;m[b] = a;}cin.get();while (cin.getline(ch,3005)){p = ch;if (ch[0] == 'E')break;while (ch[0] != '\0'){b = "";while (ch[0] >= 'a' && ch[0] <= 'z')b += ch[0], ch++;if (m.find(b) == m.end())cout << b;else cout << m[b];while (ch[0] != '\0' && ch[0] < 'a' || ch[0] > 'z'){cout << ch[0];ch++;}}cout << endl;ch = p;}return 0;
}

AC代码二(字典树实现):

#include <iostream>
#include<string>
#include<string.h>
#include<vector>
using namespace std;string a[1000000], b;
int ka = 0;int main()
{char *ch = new char[3005], *p;cin >> b;Trie<char> t;while (cin >> a[++ka] >> b){if (a[ka][0] == 'E')break;t.push(b.data(),b.length(),ka);}cin.get();while (cin.getline(ch, 3005)){p = ch;if (ch[0] == 'E')break;while (ch[0] != '\0'){b = "";while (ch[0] >= 'a' && ch[0] <= 'z')b += ch[0], ch++;if (t.find(b.data(), b.length()) == 0)cout << b;else cout << a[t.find(b.data(), b.length())];while (ch[0] != '\0' && ch[0] < 'a' || ch[0] > 'z'){cout << ch[0];ch++;}}cout << endl;ch = p;}return 0;
}

PS:a数组不能开小了,10万是不够的,100万是够的。

力扣 1804. 实现 Trie (前缀树) II

前缀树(trie ,发音为 "try")是一个树状的数据结构,用于高效地存储和检索一系列字符串的前缀。前缀树有许多应用,如自动补全和拼写检查。

实现前缀树 Trie 类:

Trie() 初始化前缀树对象。
void insert(String word) 将字符串 word 插入前缀树中。
int countWordsEqualTo(String word) 返回前缀树中字符串 word 的实例个数。
int countWordsStartingWith(String prefix) 返回前缀树中以 prefix 为前缀的字符串个数。
void erase(String word) 从前缀树中移除字符串 word 。

示例 1:

输入
["Trie", "insert", "insert", "countWordsEqualTo", "countWordsStartingWith", "erase", "countWordsEqualTo", "countWordsStartingWith", "erase", "countWordsStartingWith"]
[[], ["apple"], ["apple"], ["apple"], ["app"], ["apple"], ["apple"], ["app"], ["apple"], ["app"]]
输出
[null, null, null, 2, 2, null, 1, 1, null, 0]

解释
Trie trie = new Trie();
trie.insert("apple");               // 插入 "apple"。
trie.insert("apple");               // 插入另一个 "apple"。
trie.countWordsEqualTo("apple");    // 有两个 "apple" 实例,所以返回 2。
trie.countWordsStartingWith("app"); // "app" 是 "apple" 的前缀,所以返回 2。
trie.erase("apple");                // 移除一个 "apple"。
trie.countWordsEqualTo("apple");    // 现在只有一个 "apple" 实例,所以返回 1。
trie.countWordsStartingWith("app"); // 返回 1
trie.erase("apple");                // 移除 "apple"。现在前缀树是空的。
trie.countWordsStartingWith("app"); // 返回 0

提示:

1 <= word.length, prefix.length <= 2000
word 和 prefix 只包含小写英文字母。
insert、 countWordsEqualTo、 countWordsStartingWith 和 erase 总共调用最多 3 * 104 次。
保证每次调用 erase 时,字符串 word 总是存在于前缀树中。

class Trie {
public:Trie() {memset(ans[key], 0, sizeof(ans[key]));}void insert(string s){int i, j;for (i = 0, j = 0; s[i]; i++){if (ans[j][s[i] - 'a' + 1] == 0){ans[j][s[i] - 'a' + 1] = ++key;memset(ans[key], 0, sizeof(ans[key]));}j = ans[j][s[i] - 'a' + 1];}ans[j][0] = ka;m[j]++;}int find(string s){int i, j;for (i = 0, j = 0; s[i]; i++){if (ans[j][s[i] - 'a' + 1] == ka)return 0;j = ans[j][s[i] - 'a' + 1];}return j;}int countWordsEqualTo(string s) {return m[find(s)];}int dfs(int id){int s = m[id];for (int i = 1; i <= 26; i++)if (ans[id][i])s += dfs(ans[id][i]);return s;}int countWordsStartingWith(string s) {if (find(s) == 0)return 0;return dfs(find(s));}void erase(string s) {m[find(s)]--;}int ans[50000][27];map<int, int>m;int key = 0;int ka = 0;
};

力扣 139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅有小写英文字母组成
  • wordDict 中的所有字符串 互不相同

class Solution {
public:bool has(string s){if (s.empty())return true;if (dp[s.length()] == 1)return true;if (dp[s.length()] == 2)return false;int maxDeep;vector<int>ends;t.find(s.data(), s.length(), maxDeep, ends);for (auto id : ends) {if (has(s.substr(t.deep[id], s.length() - t.deep[id]))) {dp[s.length()] = 1;return true;}}dp[s.length()] = 2;return false;}bool wordBreak(string s, vector<string>& wordDict) {for (auto s : wordDict)t.push(s.data(), s.length());return has(s);}Trie<char>t;map<int, int>dp;
};

力扣 140. 单词拆分 II

给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

注意:词典中的同一个单词可能在分段中被重复使用多次。

示例 1:

输入:s = "catsanddog", wordDict = ["cat","cats","and","sand","dog"]
输出:["cats and dog","cat sand dog"]

示例 2:

输入:s = "pineapplepenapple", wordDict = ["apple","pen","applepen","pine","pineapple"]
输出:["pine apple pen apple","pineapple pen apple","pine applepen apple"]
解释: 注意你可以重复使用字典中的单词。

示例 3:

输入:s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
输出:[]

提示:

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s 和 wordDict[i] 仅有小写英文字母组成
  • wordDict 中所有字符串都 不同
class Solution {
public:vector<string> solve(string s){if (s == "")return vector<string>(1);if (dp.find(s.length())!=dp.end())return dp[s.length()];int maxDeep;vector<int>ends;t.find(s.data(), s.length(), maxDeep, ends);for (auto id : ends) {vector<string> v = solve(s.substr(t.deep[id], s.length() - t.deep[id]));for (auto &vi : v) {dp[s.length()].push_back(s.substr(0, t.deep[id]) + " " + vi);}}return dp[s.length()];}vector<string> wordBreak(string s, vector<string>& wordDict) {for (auto s : wordDict)t.push(s.data(), s.length());vector<string> v= solve(s);for (auto &vi : v)vi.erase(vi.begin() + vi.length() - 1);return v;}Trie<char>t;map<int, vector<string>>dp;
};

字典树(Trie,前缀树)相关推荐

  1. 字典树/Trie/前缀树-LeetCode总结:720词典中最长的单词;127. 单词接龙;677. 键值映射;面试题 17.17. 多次搜索;648. 单词替换

    MyTrie结构体和相关操作函数 typedef struct MyTrie {bool is_word;vector<MyTrie*> next;MyTrie():is_word(fal ...

  2. leetcode 676. Implement Magic Dictionary | 676. 实现一个魔法字典(DFS+Trie 前缀树)

    题目 https://leetcode.com/problems/implement-magic-dictionary/description/ 题解 题意理解 前缀树问题,大意是是让你在字典中找到是 ...

  3. 分门别类刷leetcode——高级数据结构(字典树,前缀树,trie树,并查集,线段树)

    目录 Trie树(字典树.前缀树)的基础知识 字典树的节点表示 字典树构造的例子 字典树的前序遍历 获取字典树中全部单词 字典树的整体功能 字典树的插入操作 字典树的搜索操作 字典树的前缀查询 字典树 ...

  4. 实现字典树(前缀树、Trie树)并详解其应用

    今天看到一个比较好的数据结构,字典树,做一下记录,以供自己后期复习和读者朋友的参考. 1.定义 字典树又称单词查找树.前缀树.Trie树等,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序 ...

  5. trie(字典树、前缀树)

    trie(字典树.前缀树) 1. trie原理 原理 trie树,又被称为字典树.前缀树,是一种高效地存储和查找字符串集合的数据结构. 一般来说,用到trie的题目中的字母要么全是小写字母,要么全是大 ...

  6. 208. 实现 Trie (前缀树)

    208. 实现 Trie (前缀树) Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键.这一数据结构有相当多的应用情景,例 ...

  7. Leetcode 208.实现 Trie (前缀树)(Implement Trie (Prefix Tree))

    Leetcode 208.实现 Trie (前缀树) 1 题目描述(Leetcode题目链接)   实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三 ...

  8. Leetcode典型题解答和分析、归纳和汇总——T208(实现Trie前缀树)

    问题描述: 实现一个Trie前缀树,包含insert.search和startsWith这三个操作. 问题分析: 这类的题目与堆栈的最小元素查找类似,将所有功能进行集中处理. 首先我们需要明确一下tr ...

  9. LeetCode 208. 实现 Trie (前缀树) —— 提供一套前缀树模板

    208. 实现 Trie (前缀树) Ideas 前缀树嘛,直接套模板咯,把之前写的拿过来抄一遍. 提供一下我的模板. Code Python class TrieNode:def __init__( ...

  10. leetcode 677. Map Sum Pairs | 677. 键值映射(Trie前缀树,BFS)

    题目 https://leetcode.com/problems/map-sum-pairs/ 题解 基于前缀树实现,可以参考:leetcode 208. Implement Trie (Prefix ...

最新文章

  1. Linux定时器使用
  2. 二十三、死锁的处理策略---避免死锁(银行家算法)
  3. 基于深度学习识别模型的缺陷检测
  4. Apache OpenWebBeans 发布适用于 Java EE 微服务的 Meecrowave 服务器 1.0
  5. 用内存流 文件流 资源生成客户端(Delphi开源)
  6. Leecode 260. 只出现一次的数字 III——Leecode每日一题系列
  7. mysql使用sql语句查询数据库所有表注释已经表字段注释
  8. chromedriver与chrome各版本及下载地址,截止到2018.5.30
  9. 商城小程序、实例原型设计、电商app、积分商城、领券中心、会员中心、每日签到、小程序电商、优惠券、移动端电商、Axure原型、rp原型、产品原型、积分、会员卡
  10. Mybatis之执行自定义SQL举例
  11. iOS App被拒原因以及解决方案总结。
  12. Tomcat乱码解决方法
  13. 读文献——《Going deeper with convolutions》
  14. xmanager连接linux7桌面,使用Xmanager连接CentOS 7远程桌面
  15. Wireshark 解析PDCP-LTE
  16. datagirdview的单元格双击事件
  17. Redis中set、setnx、setex区别
  18. UTF8 To GBK 转换程序
  19. 鸟哥谈云原生安全最佳实践
  20. jaeger链路追踪php,使用 Jaeger 完成服务间的链路追踪

热门文章

  1. 女人为什么要嫁人?(转)
  2. 在需求评审,测试需要关注的四类问题
  3. [Spark的二次排序的实现]
  4. 广西省崇左市谷歌卫星地图下载
  5. 集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)(生成函数)(组合数学)
  6. 原来PID是在老王头和老斯基的斗争中诞生的
  7. ThreadPoolExecutor 线程池的七个参数
  8. CMT2300A 是一款超低功耗,高性能,适用于各种 127 至 1020 MHz 无线应用
  9. MyBatis执行简单的CRUD
  10. 工程流体力学笔记暂记18(二元漩涡的速度和压强分布)