648. 单词替换
-
题目: 在英语中,我们有一个叫做 词根(root) 的概念,可以词根后面添加其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。
现在,给定一个由许多词根组成的词典 dictionary 和一个用空格分隔单词形成的句子 sentence。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
你需要输出替换之后的句子。示例1: 输入:dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery" 输出:"the cat was rat by the bat" 示例2: 输入:dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs" 输出:"a a b c"
-
解题思路:
- 暴力法:
建立两层循环,外循环遍历sentence数组,内循环遍历dictionary数组,当sentence中的某个元素以dictionary中的某个元素开头时,则写入结果数组中,如果遍历到后期,dictionary数组中的某个短的词根也在sentence的开头,则用最短的替换,最后组合结果,用空格隔开即可。 - 字典树:
利用dicrionary中的所有词根构造一棵字典树,并用特殊符号标记结尾。在搜索前缀时,只需在字典树上搜索出一条最短的前缀路径即可。
- 暴力法:
-
代码:
// 解法1:暴力法 public class Solution { public string ReplaceWords(IList<string> dictionary, string sentence) { string[] sentences = sentence.Split(' '); string[] results = new string[sentences.Length]; for(int i = 0; i < sentences.Length; i++){ foreach(string s in dictionary){ if(sentences[i].StartsWith(s)){ if(string.IsNullOrEmpty(results[i])){ results[i] = s; } else{ if(s.Length < results[i].Length){ results[i] = s; } } } } if(string.IsNullOrEmpty(results[i])){ results[i] = sentences[i]; } } return string.Join(" ", results); } } // 解法2:字典树 public class Solution { public string ReplaceWords(IList<string> dictionary, string sentence) { Trie trie = new Trie(); foreach(string word in dictionary){ Trie cur = trie; for(int i = 0; i < word.Length; i++){ char c = word[i]; if(!cur.Children.ContainsKey(c)){ cur.Children.Add(c, new Trie()); } cur = cur.Children[c]; } cur.Children.Add('#', new Trie()); } string[] words = sentence.Split(' '); for(int i = 0; i < words.Length; i++){ words[i] = FindRoot(words[i], trie); } return string.Join(" ", words); } public string FindRoot(string word, Trie trie){ StringBuilder root = new StringBuilder(); Trie cur = trie; for(int i = 0; i < word.Length; i++){ char c = word[i]; if(cur.Children.ContainsKey('#')){ return root.ToString(); } if(!cur.Children.ContainsKey(c)){ return word; } root.Append(c); cur = cur.Children[c]; } return root.ToString(); } public class Trie{ public Dictionary<char, Trie> Children; public Trie(){ Children = new Dictionary<char, Trie>(); } } }
208. 实现 Trie (前缀树)
-
题目: Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。示例1: 输入 ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true] 解释 Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 True trie.search("app"); // 返回 False trie.startsWith("app"); // 返回 True trie.insert("app"); trie.search("app"); // 返回 True
-
解题思路:
- 数组实现:
前缀树(字典树)是一种多叉树,主要用于存储和查找字符串。假设只存储小写字母,则每一个父节点对应拥有26个子节点(即从a~z),以及包含是否为一个字符串结束的标志。
-
哈希表实现:
前缀树(字典树)是一种多叉树,主要用于存储和查找字符串。定义一个哈希表结构的字典树,其中key为字符,value为子树。
-
代码:
// 解法1:数组实现 public class Trie { bool isEnd; Trie[] Children = new Trie[26]; public Trie() { } public void Insert(string word) { Trie cur = this; for(int i = 0; i < word.Length; i++){ cur.Children[word[i] - 'a'] ??= new Trie(); cur = cur.Children[word[i] - 'a']; } cur.isEnd = true; } public bool Search(string word) { Trie cur = this; for(int i = 0; i < word.Length; i++){ if(cur.Children[word[i] - 'a'] == null){ return false; } cur = cur.Children[word[i] - 'a']; } return cur.isEnd; } public bool StartsWith(string prefix) { Trie cur = this; for(int i = 0; i < prefix.Length; i++){ if(cur.Children[prefix[i] - 'a'] == null){ return false; } cur = cur.Children[prefix[i] - 'a']; } return true; } } /** * Your Trie object will be instantiated and called as such: * Trie obj = new Trie(); * obj.Insert(word); * bool param_2 = obj.Search(word); * bool param_3 = obj.StartsWith(prefix); */ // 解法2:哈希表实现 public class Trie { Dictionary<char, Trie> Children; public Trie() { this.Children = new Dictionary<char, Trie>(); } public void Insert(string word) { Trie cur = this; for(int i = 0; i < word.Length; i++){ if(!cur.Children.ContainsKey(word[i])){ cur.Children.Add(word[i], new Trie()); } cur = cur.Children[word[i]]; } if(!cur.Children.ContainsKey('#')){ cur.Children.Add('#', new Trie()); } } public bool Search(string word) { Trie cur = this; for(int i = 0; i < word.Length; i++){ if(!cur.Children.ContainsKey(word[i])){ return false; } cur = cur.Children[word[i]]; } if(cur.Children.ContainsKey('#')){ return true; } return false; } public bool StartsWith(string prefix) { Trie cur = this; for(int i = 0; i < prefix.Length; i++){ if(!cur.Children.ContainsKey(prefix[i])){ return false; } cur = cur.Children[prefix[i]]; } return true; } } /** * Your Trie object will be instantiated and called as such: * Trie obj = new Trie(); * obj.Insert(word); * bool param_2 = obj.Search(word); * bool param_3 = obj.StartsWith(prefix); */