在英语中,我们有一个叫做 词根
(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词
(successor)。例如,词根an
,跟随着单词 other
(其他),可以形成新的单词 another
(另一个)。
现在,给定一个由许多词根组成的词典和一个句子。你需要将句子中的所有继承词
用词根
替换掉。如果继承词
有许多可以形成它的词根
,则用最短的词根替换它。
你需要输出替换之后的句子。
示例 1:
输入: dict(词典) = ["cat", "bat", "rat"] sentence(句子) = "the cattle was rattled by the battery" 输出: "the cat was rat by the bat"
注:
- 输入只包含小写字母。
- 1 <= 字典单词数 <=1000
- 1 <= 句中词语数 <= 1000
- 1 <= 词根长度 <= 100
- 1 <= 句中词语长度 <= 1000
思路:这道题用单词前缀树来做,但是题目要求如果有多个解,保留最短的单词长度即可,所以有两个优化的地方:
1:每次通过bool变量的isRoot来匹配是否存在最短路径,例如cat和cata同时作为词根进入前缀树,那么前缀树只会构造出cat而不会构造出cata(严格来说和cat和cata谁先进入有关系,但是最终的结果是通过isRoot变量来保证在查找匹配字符串时只匹配cat)
2:如果没有找到以当前单词为词根的字符串,那么光标会持续向右移,所以这样可以保证时间复杂度是O(1)
参考代码:
class Solution {
public:
class trie {
private:
bool isRoot=false;
trie* l[26] = {};
public:
void insert(string& word, int start, int end) {
isRoot |= (start == end);
if (!isRoot) {
if (l[word[start] - 'a'] == nullptr) l[word[start] - 'a'] = new trie();
l[word[start] - 'a']->insert(word, start + 1, end);
}
}
int calculate(string& word, int start, int ch, int end) {
if ((start+ch)==end || word[start+ch]==' ' || isRoot) return ch;
if (l[word[start + ch] - 'a'] == nullptr) { //没有找到root
while ((start + ch) < end && word[start + ch] != ' ') ch++;
return ch;
}
return l[word[start + ch] - 'a']->calculate(word, start, ch + 1, end);
}
};
string replaceWords(vector<string>& dict, string sentence) {
trie t;
string res;
for (auto &tmp : dict) t.insert(tmp,0,tmp.size());
for (int i = 0; i < sentence.size();) {
if (sentence[i] == ' ') res += sentence[i++];
int tmp = t.calculate(sentence, i, 0, sentence.size());
res += sentence.substr(i, tmp);
for (i += tmp; i < sentence.size() && sentence[i] != ' ';) i++;
}
return res;
}
};