LeetCode每日一题(20220707)

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"
    
  • 解题思路:

    1. 暴力法:
      建立两层循环,外循环遍历sentence数组,内循环遍历dictionary数组,当sentence中的某个元素以dictionary中的某个元素开头时,则写入结果数组中,如果遍历到后期,dictionary数组中的某个短的词根也在sentence的开头,则用最短的替换,最后组合结果,用空格隔开即可。
    2. 字典树:
      利用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
    

  • 解题思路:

    1. 数组实现:

    ​ 前缀树(字典树)是一种多叉树,主要用于存储和查找字符串。假设只存储小写字母,则每一个父节点对应拥有26个子节点(即从a~z),以及包含是否为一个字符串结束的标志。

    1. 哈希表实现:

      ​ 前缀树(字典树)是一种多叉树,主要用于存储和查找字符串。定义一个哈希表结构的字典树,其中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);
     */
    
赞赏