Problem: 127. 单词接龙
好好好!又做出来一道困难题,逐渐在进步,莫西莫西!!!
此题用到了广度优先搜索遍历,以及哈希表的运用。c++中哈希表是unordered_map。如果对此不了解的uu,建议查看相关介绍博客和更简单的题目!!!
该题解法为:广度优先搜索遍历 + 哈希表的运用。
时间复杂度:
O(260 * n)
空间复杂度:
O(5000 * 3),只计算了mm,us,fd。
class Solution {
public:
unordered_map<string, vector<string>> mm;
vector<string> ffdd(string& s, unordered_set<string>& us) {
vector<string> v;
//比如s为hot,那么我选择遍历每个位置的26个字母的话最坏复杂度只有s.size() * 26,即10 * 26,但是我如果选择遍历wordlist中的话,就麻烦多了,首先是5000的长度,况且找到之后怎么确定是s的下一个状态,时间复杂度肯定不是O(1)。因此如果用一个哈希表记录wordlist,在用260的复杂度便可以找到s的下一个状态的所有字符串。
for (int i = 0; i < s.size(); i++) { //260
for (int j = 0; j < 26; j++) {
if ('a' + j != s[i]) {
string ss(s);
ss[i] = 'a' + j;
if (us.count(ss)) {
v.push_back(ss);
}
}
}
}
return v;
}
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string> us, fd;
for (auto& s : wordList) { //n
//我们先用一个哈希集合去存储wordlist所有字符串,让之后的查询复杂度降为O(1)。
us.insert(s);
}
for (auto& s : wordList) { // 260 * n
//再次遍历wordlist,对每个s的下一个状态进行查询,复杂度为260,并且将每个s的下一个状态存储到一个哈希表mm(unordered_map<string, vector<string>>)中,具有记忆化功能,之后查询为O(1)。
mm.insert({s, ffdd(s, us)});
}
int cnt = 0;
//进行广度优先搜索的存储节点的队列
queue<string> q;
//对于beginword,我单独进行了一次查找下一个状态的操作
for (int i = 0; i < beginWord.size(); i++) { //260
for (int j = 0; j < 26; j++) {
if ('a' + j != beginWord[i]) {
string ss(beginWord);
ss[i] = 'a' + j;
if (us.count(ss)) {
q.push(ss);
//再使用一个哈希集合确保是beginword的下一个状态并且进入队列的字符串不会再次被放入队列中,这样的话push的操作最多执行5000次。
fd.insert(ss);
}
}
}
}
cnt++;
while (!q.empty()) {
int size = q.size();
//5000
cnt++;
for (int ii = 0; ii < size; ii++) {
string s = q.front();q.pop();
//cnt必须先++,因为这找到endword就会return了
if (s == endWord) return cnt;
for (int i = 0; i < mm[s].size(); i++) {
if (fd.count(mm[s][i]) == 0) {
q.push(mm[s][i]);
//再使用一个哈希集合确保是mm[s][i]的下一个状态并且进入队列的字符串不会再次被放入队列中,这样的话push的操作最多执行5000次。
fd.insert(mm[s][i]);
}
}
}
}
return 0;
}
};
可以尝试用输出日志的方式来获得局部代码的正确性。对于比较长的代码,我们应该在写完整个代码之前,已经完成多个地方的日志输出。多加练习能够提高自己写代码的正确性。
for (auto& [a, b] : pp) {
//调试bug的时候可以用输出的方法
cout << a << b << endl;
}