MyTrie结构体和相关操作函数
typedef struct MyTrie {
bool is_word;
vector<MyTrie*> next;
MyTrie():is_word(false) {
next.resize(26,nullptr);
}
}MT;
void insertWord(MT* cur, const string& word) {
for (auto &w:word) {
int c = w - 'a';
if (!cur->next[c]) {
auto tmp = new MT();
cur->next[c] = tmp;
}
cur = cur->next[c];
}
cur->is_word = true;
}
bool hasWord(MT* cur, const string& word) {
for (auto &w: word) {
int c = w - 'a';
if (!cur->next[c]) return false;
cur = cur->next[c];
}
return cur->is_word;
}
vector<string> findChangeNeighbor(MT* cur, const string& word) { // 127. 单词接龙
vector<string> res;
for (int i = 0; i < word.size(); ++i) {
int c = word[i] - 'a';
for (int j = 0; j < 26; ++j) {
if (c == j) continue;
if (cur->next[c] && hasWord(cur->next[c], word.substr(i + 1))) {
auto tmp = word;
tmp[i] = j + 'a';
res.push_back(move(tmp));
}
}
cur = cur->next[c];
}
return res;
}
bool isEveryStepWord(MT* cur, const string& word) {
for (auto &w: word) {
int c = w - 'a';
if (!cur->next[c] || !cur->next[c]->is_word) return false;
}
return true;
}
int getEverySetpWordMaxLength(MT* cur, const string& word) {
int max_length = -1;
queue<MT*>q;
q.push(cur);
while (q.size()) {
auto qs = q.size();
++max_length;
while (qs--) {
auto tmp = q.front();
q.pop();
for (auto &i: tmp->next) {
if (i && i->is_word) {
q.push(i);
}
}
}
}
return max_length;
}
void findMaxLengthWordWithDFS(MT* cur, string& res_string, string path) { // 720. 词典中最长的单词
if (path.size() > res_string.size()) {
res_string = path;
}
for (int i = 0; i < 26; ++i) {
auto w = cur->next[i];
if (!w || !w->is_word) continue;
findMaxLengthWordWithDFS(w, res_string, path + static_cast<char>(i + 'a'));
}
}
vector<string> findWordStartWithPrefix(MT* cur, const string& prefix) { // 677. 键值映射
vector<string> res;
for (int i = 0; i < prefix.size(); ++i) {
int c = prefix[i] - 'a';
if (!cur->next[c]) return {};
cur = cur->next[c];
}
queue<pair<MT*, string>> q;
q.push(make_pair(cur, prefix));
while (q.size()) {
auto qs = q.size();
while (qs--) {
auto [le, ri] = q.front();
q.pop();
if (le->is_word)
res.push_back(ri);
for (int i = 0; i < 26; ++i) {
if (!le->next[i]) continue;
q.push(make_pair(le->next[i], ri + static_cast<char>(i + 'a')));
}
}
}
return res;
}
void findEveryWordPosition(MT* cur, const string& target, // 面试题 17.17. 多次搜索
vector<vector<int>>& res_pos,
unordered_map<string, int>& word_to_id) {
for (int i = 0; i < target.size(); ++i) { // i:pos_start
int cur_i =target[i] - 'a';
if (!cur->next[cur_i]) continue;
auto cur_sub = cur->next[cur_i];
for (int j = i + 1; j <= target.size(); ++j) { // j:pos_end
if (cur_sub->is_word) {
res_pos[word_to_id[target.substr(i, j - i)]].push_back(i);
}
if (j == target.size()) break;
int cur_sub_i = target[j] - 'a';
if (!cur_sub->next[cur_sub_i]) break;
cur_sub = cur_sub->next[cur_sub_i];
}
}
}
string findMinPrefix(MT* cur, const string& word) { // 648. 单词替换
string min_prefix;
for (auto &w: word) {
int c = w - 'a';
if (!cur->next[c]) return word;
min_prefix += w;
if (cur->next[c]->is_word) return min_prefix;
cur = cur->next[c];
}
return min_prefix;
}
给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。
若无答案,则返回空字符串。
示例 1:
输入:
words = ["w","wo","wor","worl", "world"]
输出:"world"
解释:
单词"world"可由"w", "wo", "wor", 和 "worl"添加一个字母组成。
示例 2:
输入:
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
输出:"apple"
解释:
"apply"和"apple"都能由词典中的单词组成。但是"apple"的字典序小于"apply"。
提示:
所有输入的字符串都只包含小写字母。
words数组长度范围为[1,1000]。
words[i]的长度范围为[1,30]。
class Solution {
public:
string longestWord(vector<string>& words) {
auto mt = new MT();
for (auto &w: words)
insertWord(mt, w);
string res_string;
findMaxLengthWordWithDFS(mt, res_string, "");
return res_string;
}
};
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:
如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
返回它的长度 5。
示例 2:
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。
class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
if (beginWord.size() == 0 || beginWord.size() != endWord.size()) return 0;
int min_cnt = 1;
MyTire *allWord = new MyTire(), *visited = new MyTire();
insertWord(allWord, beginWord);
for (int i = 0; i<wordList.size(); ++i)
insertWord(allWord, wordList[i]);
if (!hasWord(allWord, endWord)) return 0;
queue<string> q;
q.push(beginWord);
while (q.size()) {
int qSize = q.size();
++min_cnt;
while (qSize--) {
auto cur = q.front();
q.pop();
vector<string> vec = findChangeNeighber(allWord, cur);
for (int i = 0; i<vec.size(); ++i) {
if (hasWord(visited, vec[i])) continue;
if (vec[i] == endWord) return min_cnt;
q.push(vec[i]);
insertWord(visited, vec[i]);
}
}
}
return 0;
}
};
实现一个 MapSum 类里的两个方法,insert 和 sum。
对于方法 insert,你将得到一对(字符串,整数)的键值对。字符串表示键,整数表示值。如果键已经存在,那么原来的键值对将被替代成新的键值对。
对于方法 sum,你将得到一个表示前缀的字符串,你需要返回所有以该前缀开头的键的值的总和。
示例 1:
输入: insert("apple", 3), 输出: Null
输入: sum("ap"), 输出: 3
输入: insert("app", 2), 输出: Null
输入: sum("ap"), 输出: 5
class MapSum {
unordered_map<string, int> m;
MT *mt;
public:
/** Initialize your data structure here. */
MapSum() {
mt = new MT();
}
void insert(string key, int val) {
m[key] = val;
insertWord(mt, key);
}
int sum(string prefix) {
auto vec = findWordStartWithPrefix(mt, prefix);
int res_sum = 0;
for (auto &v: vec) {
res_sum += m[v];
}
return res_sum;
}
};
给定一个较长字符串big和一个包含较短字符串的数组smalls,设计一个方法,根据smalls中的每一个较短字符串,对big进行搜索。输出smalls中的字符串在big里出现的所有位置positions,其中positions[i]为smalls[i]出现的所有位置。
示例:
输入:
big = "mississippi"
smalls = ["is","ppi","hi","sis","i","ssippi"]
输出: [[1,4],[8],[],[3],[1,4,7,10],[5]]
提示:
0 <= len(big) <= 1000
0 <= len(smalls[i]) <= 1000
smalls的总字符数不会超过 100000。
你可以认为smalls中没有重复字符串。
所有出现的字符均为英文小写字母。
class Solution {
public:
vector<vector<int>> multiSearch(string big, vector<string>& smalls) {
vector<vector<int>> res_vec(smalls.size());
if (big.empty() || smalls.empty()) return res_vec;
unordered_map<string, int> word_to_id;
auto mt = new MT();
for (int i = 0; i < smalls.size(); ++i) {
word_to_id[smalls[i]] = i;
insertWord(mt, smalls[i]);
}
findEveryWordPosition(mt, big, res_vec, word_to_id);
return res_vec;
}
};
在英语中,我们有一个叫做 词根(root)的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如,词根an,跟随着单词 other(其他),可以形成新的单词 another(另一个)。
现在,给定一个由许多词根组成的词典和一个句子。你需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
你需要输出替换之后的句子。
示例:
输入:dict(词典) = ["cat", "bat", "rat"] sentence(句子) = "the cattle was rattled by the battery"
输出:"the cat was rat by the bat"
提示:
输入只包含小写字母。
1 <= dict.length <= 1000
1 <= dict[i].length <= 100
1 <= 句中词语数 <= 1000
1 <= 句中词语长度 <= 1000
class Solution {
public:
string replaceWords(vector<string>& dictionary, string sentence) {
auto mt = new MT();
for (auto &d: dictionary) {
insertWord(mt, d);
}
stringstream ss(sentence);
string res_string, s;
while (getline(ss, s, ' ')) {
res_string += findMinPrefix(mt, s) + ' ';
}
res_string.pop_back();
return res_string;
}
};