-
Notifications
You must be signed in to change notification settings - Fork 0
127. Word Ladder #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
127. Word Ladder #34
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| /* | ||
|
|
||
| Solve Time : over 1 hours | ||
|
|
||
| Time : O(N^2 * log N) | ||
| Space : O(N^2) | ||
|
|
||
| 単語同士の距離から隣接リストを形成して探索すれば良い、というところまではすぐにたどり着いたが | ||
| 微妙なミスを連発してしまい、時間がかかってしまった。 | ||
|
|
||
| priority_queueのデフォルが最大を取り出す仕様を失念していた | ||
| 最短距離を出す探索方法をしばらく失念しており、間違ったコードを書いていた | ||
|
|
||
| */ | ||
| class Solution { | ||
| public: | ||
| int ladderLength(string beginWord, string endWord, vector<string>& wordList) { | ||
| bool no_begin = std::none_of(wordList.begin(), wordList.end(), [beginWord](string s) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. のほうがシンプルだと思います。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. その書き方は思いつきませんでした、ありがとうございます。 |
||
| return s == beginWord; | ||
| }); | ||
| if (no_begin) { | ||
| wordList.push_back(beginWord); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C++の仕様が分かってないので自信ないのですが、wordListを参照で引数に渡しているので、このコードは入力が破壊される形になってますでしょうか
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. はい、入力を変更しています。 条件次第で1単語追加、程度であればまぁ許容するかなというのと There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、説明くださりありがとうございます。(ProsConsを考えられているのでよさそうです) |
||
| } | ||
| bool no_end = std::none_of(wordList.begin(), wordList.end(), [endWord](string s) { | ||
| return s == endWord; | ||
| }); | ||
| if (no_end) { | ||
| return 0; | ||
| } | ||
| vector<set<int>> adjcent_indexes = vector<set<int>>(wordList.size(), set<int>{}); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
スペルミスがあるようです。 adjcent→adjacent
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、ありがとうございます! |
||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| for (int j = 0; j < wordList.size(); ++j) { | ||
| string left = wordList[i]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コピーを減らすため、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます、このあたりコピーを減らす感覚がまだきちんとできていませんでした。 |
||
| string right = wordList[j]; | ||
| if (!IsNextWord(left, right)) { | ||
| continue; | ||
| } | ||
| adjcent_indexes[i].insert(j); | ||
| adjcent_indexes[j].insert(i); | ||
| } | ||
| } | ||
| int begin_index; | ||
| int end_index; | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| if (wordList[i] == beginWord) { | ||
| begin_index = i; | ||
| } | ||
| if (wordList[i] == endWord) { | ||
| end_index = i; | ||
| } | ||
| } | ||
| return CountDistance(begin_index, end_index, adjcent_indexes); | ||
| } | ||
|
|
||
| private: | ||
| bool IsNextWord(const string left, const string right) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちらもコピーを減らすため、 const string& で受けたほうが良いと思います。詳しくは『Effective C++ 第3版』『20項 値渡しよりconst参照渡しを使おう』をご覧ください。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます、参考にします。 |
||
| int count = 0; | ||
| for (int i = 0; i < left.size(); ++i) { | ||
| if (left[i] != right[i]) { | ||
| ++count; | ||
| } | ||
| if (count > 1) { | ||
| return false; | ||
| } | ||
| } | ||
| if (count == 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。 |
||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| int CountDistance(int begin_index, int end_index, vector<set<int>>& adjcent_indexes) { | ||
| priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> next_indexes; | ||
| next_indexes.emplace(1, begin_index); | ||
| vector<int> distances(adjcent_indexes.size(), std::numeric_limits<int>::max()); | ||
| distances[begin_index] = 1; | ||
| while (!next_indexes.empty()) { | ||
| auto [distance, index] = next_indexes.top(); | ||
| next_indexes.pop(); | ||
| if (distances[index] < distance) { | ||
| continue; | ||
| } | ||
| if (index == end_index) { | ||
| return distance; | ||
| } | ||
| distances[index] = distance; | ||
| for (auto next_index : adjcent_indexes[index]) { | ||
| if (distances[next_index] < distance + 1) { | ||
| continue; | ||
| } | ||
| next_indexes.emplace(distance + 1, next_index); | ||
| } | ||
| } | ||
| if (distances[end_index] == std::numeric_limits<int>::max()) { | ||
| return 0; | ||
| } | ||
| return distances[end_index]; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
|
|
||
| Time : O(L * N^2) | ||
| Space : O(N^2) | ||
|
|
||
| - 全てのノード間の距離が同じなのでpriority_queueは不要で、queueで十分 | ||
| - 隣接リストを作る際に、i < jの条件を追加して計算量を減らす | ||
| - BFSにて、queueが空になったときは到達不能と判断して良いのでif分を削除 | ||
| */ | ||
| class Solution { | ||
| public: | ||
| int ladderLength(string beginWord, string endWord, vector<string>& wordList) { | ||
| bool no_begin = std::none_of(wordList.begin(), wordList.end(), [beginWord](string s) { | ||
| return s == beginWord; | ||
| }); | ||
| if (no_begin) { | ||
| wordList.push_back(beginWord); | ||
| } | ||
| bool no_end = std::none_of(wordList.begin(), wordList.end(), [endWord](string s) { | ||
| return s == endWord; | ||
| }); | ||
| if (no_end) { | ||
| return 0; | ||
| } | ||
|
Comment on lines
+19
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これ、現代的な書き方ですが、これは for で書いたほうが読みやすくないでしょうか。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。 |
||
| vector<set<int>> adjacent_indexes = vector<set<int>>(wordList.size(), set<int>{}); | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| for (int j = i + 1; j < wordList.size(); ++j) { | ||
| string left = wordList[i]; | ||
| string right = wordList[j]; | ||
| if (!IsNextWord(left, right)) { | ||
|
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここでコピーが4回走りますね。参照でよいでしょう。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます、無駄なコピーをしていました。 |
||
| continue; | ||
| } | ||
| adjacent_indexes[i].insert(j); | ||
| adjacent_indexes[j].insert(i); | ||
| } | ||
| } | ||
| int begin_index; | ||
| int end_index; | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| if (wordList[i] == beginWord) { | ||
| begin_index = i; | ||
| } | ||
| if (wordList[i] == endWord) { | ||
| end_index = i; | ||
| } | ||
| } | ||
| return CountDistance(begin_index, end_index, adjacent_indexes); | ||
| } | ||
|
|
||
| private: | ||
| bool IsNextWord(const string left, const string right) { | ||
| int count = 0; | ||
| for (int i = 0; i < left.size(); ++i) { | ||
| if (left[i] != right[i]) { | ||
| ++count; | ||
| } | ||
| if (count > 1) { | ||
| return false; | ||
| } | ||
| } | ||
| if (count == 0) { | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| int CountDistance(int begin_index, int end_index, vector<set<int>>& adjacent_indexes) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. distanceというと、ここで求めているものから1を引いたものを一般に想像してしまいそうです。(グラフにした時の距離の定義もそうだと思います)
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます、確かに混乱を生む語彙でした |
||
| queue<pair<int, int>> next_indexes; | ||
| next_indexes.emplace(1, begin_index); | ||
| vector<int> distances(adjacent_indexes.size(), std::numeric_limits<int>::max()); | ||
| distances[begin_index] = 1; | ||
| while (!next_indexes.empty()) { | ||
| auto [distance, index] = next_indexes.front(); | ||
| next_indexes.pop(); | ||
| if (distances[index] < distance) { | ||
| continue; | ||
| } | ||
| if (index == end_index) { | ||
| return distance; | ||
| } | ||
| distances[index] = distance; | ||
| for (auto next_index : adjacent_indexes[index]) { | ||
| if (distances[next_index] < distance + 1) { | ||
| continue; | ||
| } | ||
| next_indexes.emplace(distance + 1, next_index); | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /* | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 参考程度に私が書いたものを貼っておきます。 class Solution {
public:
int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
unordered_set<string_view> words(wordList.begin(), wordList.end());
if (!words.contains(endWord)) {
return 0;
}
queue<string_view> que({beginWord});
auto push_adjacent_words = [&](string word) {
for (int i = 0; i < word.size(); ++i) {
char orig = word[i];
for (char c = 'a'; c <= 'z'; ++c) {
word[i] = c;
auto it = words.find(word);
if (it != words.end()) {
que.push(*it);
words.erase(it);
}
}
word[i] = orig;
}
};
for (int length = 1; !que.empty(); ++length) {
for (int size = que.size(); size; --size) {
string_view word = que.front();
que.pop();
if (word == endWord) {
return length;
}
push_adjacent_words(string(word));
}
}
return 0;
}
}; |
||
|
|
||
| Time : O(N * L * log N) | ||
| Space : O(N^2) | ||
|
|
||
| 他の人の解法を参考に改良したもの | ||
| 隣接リストの形成を改善、単語のリストを二重ループで探索するのではなく、各単語を1文字ずつa - zに変化させて、wordList内に一致する単語の有無を確認。 | ||
|
|
||
| */ | ||
| class Solution { | ||
| public: | ||
| int ladderLength(string beginWord, string endWord, vector<string>& wordList) { | ||
| bool no_end = std::none_of(wordList.begin(), wordList.end(), [endWord](string s) { | ||
| return s == endWord; | ||
| }); | ||
| if (no_end) { | ||
| return 0; | ||
| } | ||
| bool no_begin = std::none_of(wordList.begin(), wordList.end(), [beginWord](string s) { | ||
| return s == beginWord; | ||
| }); | ||
| if (no_begin) { | ||
| wordList.push_back(beginWord); | ||
| } | ||
| vector<set<int>> adjacent_indexes = vector<set<int>>(wordList.size(), set<int>{}); | ||
| ComposeAdjacentIndexes(adjacent_indexes, wordList); | ||
| int begin_index; | ||
| int end_index; | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| if (wordList[i] == beginWord) { | ||
| begin_index = i; | ||
| } | ||
| if (wordList[i] == endWord) { | ||
| end_index = i; | ||
| } | ||
| } | ||
| return CountDistance(begin_index, end_index, adjacent_indexes); | ||
| } | ||
|
|
||
| private: | ||
| void ComposeAdjacentIndexes(vector<set<int>>& adjacent_indexes, const vector<string>& wordList) { | ||
| map<string, int> word_to_index; | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| word_to_index[wordList[i]] = i; | ||
| } | ||
| for (int i = 0; i < wordList.size(); ++i) { | ||
| for (int char_index = 0; char_index < wordList[i].size(); ++char_index) { | ||
| string adjacent_word = wordList[i]; | ||
| for (char c = 'a'; c <= 'z'; ++c) { | ||
| adjacent_word[char_index] = c; | ||
| if (adjacent_word == wordList[i]) { | ||
| continue; | ||
| } | ||
| if (!word_to_index.contains(adjacent_word)) { | ||
| continue; | ||
| } | ||
| const int left = i; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 自分は定数であることを強調したいとき以外に、ローカルの変数に const は付けません。このあたりはチームの平均的な書き方に合わせることをお勧めいたします。 |
||
| const int right = word_to_index[adjacent_word]; | ||
| adjacent_indexes[left].insert(right); | ||
| adjacent_indexes[right].insert(left); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int CountDistance(int begin_index, int end_index, vector<set<int>>& adjacent_indexes) { | ||
| queue<pair<int, int>> next_indexes; | ||
| next_indexes.emplace(1, begin_index); | ||
| vector<int> distances(adjacent_indexes.size(), std::numeric_limits<int>::max()); | ||
| distances[begin_index] = 1; | ||
| while (!next_indexes.empty()) { | ||
| auto [distance, index] = next_indexes.front(); | ||
| next_indexes.pop(); | ||
| if (distances[index] < distance) { | ||
| continue; | ||
| } | ||
| if (index == end_index) { | ||
| return distance; | ||
| } | ||
| distances[index] = distance; | ||
| for (auto next_index : adjacent_indexes[index]) { | ||
| if (distances[next_index] < distance + 1) { | ||
| continue; | ||
| } | ||
| next_indexes.emplace(distance + 1, next_index); | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| class Solution { | ||
| public: | ||
| int ladderLength(string begin_word, string end_word, vector<string>& word_list) { | ||
| bool no_end = none_of(word_list.begin(), word_list.end(), [end_word](string s) { return s == end_word;}); | ||
| if (no_end) { | ||
| return 0; | ||
| } | ||
| bool no_begin = none_of(word_list.begin(), word_list.end(), [begin_word](string s) { return s == begin_word; }); | ||
| if (no_begin) { | ||
| word_list.emplace_back(begin_word); | ||
| } | ||
| vector<vector<int>> adjacent_indexes = vector<vector<int>>(word_list.size(), vector<int>{}); | ||
| ComposeAdjacentIndexes(adjacent_indexes, word_list); | ||
| int begin_index; | ||
| int end_index; | ||
| for (int i = 0; i < word_list.size(); ++i) { | ||
| if (word_list[i] == begin_word) { | ||
| begin_index = i; | ||
| } | ||
| if (word_list[i] == end_word) { | ||
| end_index = i; | ||
| } | ||
| } | ||
| return CountWordLadder(begin_index, end_index, adjacent_indexes); | ||
| } | ||
|
|
||
| private: | ||
| void ComposeAdjacentIndexes(vector<vector<int>>& adjacent_indexes, const vector<string>& word_list) { | ||
| for (int i = 0; i < word_list.size(); ++i) { | ||
| for (int j = i + 1; j < word_list.size(); ++j) { | ||
| if (!IsNextWords(word_list[i], word_list[j])) { | ||
| continue; | ||
| } | ||
| adjacent_indexes[i].emplace_back(j); | ||
| adjacent_indexes[j].emplace_back(i); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool IsNextWords(const string left, const string right) { | ||
| int diff_count = 0; | ||
| for (int i = 0; i < left.size(); ++i) { | ||
| if (left[i] != right[i]) { | ||
| ++diff_count; | ||
| } | ||
| } | ||
| return diff_count == 1; | ||
| } | ||
|
|
||
| int CountWordLadder(const int begin_index, const int end_index, const vector<vector<int>>& adjacent_indexes) { | ||
| queue<pair<int, int>> indexes_and_distances; | ||
| indexes_and_distances.emplace(begin_index, 1); | ||
| vector<int> distances = vector<int>(adjacent_indexes.size(), numeric_limits<int>::max()); | ||
| while (!indexes_and_distances.empty()) { | ||
| auto [index, distance] = indexes_and_distances.front(); | ||
| indexes_and_distances.pop(); | ||
| if (distances[index] <= distance) { | ||
| continue; | ||
| } | ||
| distances[index] = distance; | ||
| for (int next_index : adjacent_indexes[index]) { | ||
| if (next_index == end_index) { | ||
| return distance + 1; | ||
| } | ||
| indexes_and_distances.emplace(next_index, distance + 1); | ||
| } | ||
| } | ||
| return 0; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[beginWord] は呼び出しごとに毎回コピーされるのではないでしょうか。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
このあたりの挙動、ちゃんと理解できていなかったので調べました。
ラムダを引数として渡したタイミングでコピーされるので、呼び出しごとにコピーされる心配はなさそうと結論付けました。
https://ja.cppreference.com/w/cpp/language/lambda#Lambda_capture
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あ、そうですね。
呼び出しごとに関数呼び出しでコピーされているのは s のほうですか。
ここのキャプチャーは [&beginWord] か [&] くらいで十分なように思いました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
なるほど、確かに、ありがとうございます。
sは毎回コピーされるので、参照にしたほうが良さそうですね