【C/C++】648. 单词替换

230 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情


题目链接:648. 单词替换

题目描述

在英语中,我们有一个叫做 词根 (root) 的概念,可以词根 后面 添加其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根 an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。

现在,给定一个由许多 词根 组成的词典 dictionary 和一个用空格分隔单词形成的句子 sentence。你需要将句子中的所有 继承词词根 替换掉。如果 继承词 有许多可以形成它的 词根,则用 最短 的词根替换它。

你需要输出替换之后的句子。

提示:

  • 1dictionary.length 10001 \leqslant dictionary.length \leqslant 1000
  • 1dictionary[i].length1001 \leqslant dictionary[i].length \leqslant 100
  • dictionary[i] 仅由小写字母组成。
  • 1sentence.length1061 \leqslant sentence.length \leqslant 10^6
  • sentence 仅由小写字母和空格组成。
  • sentence 中单词的总量在范围 [1, 1000] 内。
  • sentence 中每个单词的长度在范围 [1, 1000] 内。
  • sentence 中单词之间由一个空格隔开。
  • 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"

整理题意

题目给定一个词根字符串数组 dictionary ,里面包含多个字符串表示词根,然后给定一个字符串 sentence 表示句子,句子中由多个单词和空格组成,让我们将句子中的单词用最短的词根代替,这里代替的要求为词根为当前单词的前缀,且要求是最短的词根。

题目规定句子 sentence 仅由小写字母和空格组成,单词的总量在范围 [1, 1000] 内,单词的长度在范围 [1, 1000] 内,单词之间由一个空格隔开,没有前导或尾随空格。

解题思路分析

首先很容易想到的是暴力解法,遍历句子 sentence 中每一个单词,然后在词根数组 dictionary 中查找最短的前缀进行替换即可。

方法一:哈希表

但是对于每个单词都要遍历一遍词根数组 dictionary 显得比较冗余,我们可以使用哈希表先将所有词根进行存储,然后在遍历句子 sentence 中每一个单词的时候,由短至长 遍历它所有的前缀(可以保证是最短的词根),第一个出现在哈希表集合中的前缀即为最短词根,将这个词根替换原来的单词。最后返回重新拼接的句子。

具体实现

方法一:哈希表

  1. 遍历词根数组,使用无序 set 集合记录每个词根;
  2. 提取句子中的所有单词放入 words
  3. 遍历 words 数组,由短至长遍历每个单词前缀;
  4. 判断是否为词根,进行替换操作;
  5. 返回重新拼接的句子。

复杂度分析

  • 时间复杂度:O(d+len)O(d + len)。其中 ddictionary 的字符数,构建哈希集合消耗 O(d)O(d) 时间。lensentence 的长度,判断单词的前缀子字符串是否位于哈希集合中消耗 O(len)O(len) 时间。
  • 空间复杂度:O(d+s)O(d + s),其中 ssentence 的字符数。构建哈希集合消耗 O(d)O(d) 空间,分割 sentence 消耗 O(s)O(s) 空间。

代码实现

方法一:哈希表

class Solution {
public:
    string replaceWords(vector<string>& dictionary, string sentence) {
        //使用无序集合 set
        unordered_set<string> mp;
        mp.clear();
        //将词根放入集合set中
        int n = dictionary.size();
        for(int i = 0; i < n; i++){
            mp.insert(dictionary[i]);
        }
        //提取句子中的每个单词
        vector<string> words;
        words.clear();
        int m = sentence.length();
        string temp = "";
        for(int i = 0; i <= m; i++){
            if(i == m || sentence[i] == ' '){
                if(temp != "") words.push_back(temp);
                temp = "";
            }
            else{
                temp += sentence[i];
            }
        }
        string ans = "";
        //由短至长遍历每个单词前缀
        int k = words.size();
        for(int i = 0; i < k; i++){
            int len = words[i].length();
            temp = "";
            for(int j = 0; j < len; j++){
                temp += words[i][j];
                //判断是否为词根
                if(mp.count(temp)){
                    break;
                }
            }
            words[i] = temp;
            if(i != k - 1) ans += temp + ' ';
            else ans += temp;
        }
        return ans;
    }
};

总结

  • 使用 set 集合比 map 集合更节省空间。
  • 创建无序的集合 unordered_set 比有序集合 set 在查找时更快。
  • 该题还可以使用 字典树 解法,与哈希集合不同,我们用 dictionary 中所有词根构建一棵字典树,并用特殊符号标记结尾。在搜索前缀时,只需在字典树上搜索出一条最短的前缀路径即可。
  • 测试结果:

53set.png

结束语

一个人的成熟,不仅在于经历过多少事,更在于经历过后的沉淀和思考。不要只顾着埋头向前冲,适时停一停,给自己一点反思的时间。从每一次的经历中有所领悟有所得到,你才能在未来的路上走得更远更稳健。新的一天,加油!