From 1df881c6a7f164ae417d1c3f5b82b732a284624f Mon Sep 17 00:00:00 2001 From: Hurukawa2121 Date: Sun, 12 Jan 2025 19:59:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Word=20Ladder=20=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 6.graph/word-ladder/memo.md | 24 +++++++++++++++ 6.graph/word-ladder/step1.cpp | 38 +++++++++++++++++++++++ 6.graph/word-ladder/step2.cpp | 57 +++++++++++++++++++++++++++++++++++ 6.graph/word-ladder/step3.cpp | 57 +++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 6.graph/word-ladder/memo.md create mode 100644 6.graph/word-ladder/step1.cpp create mode 100644 6.graph/word-ladder/step2.cpp create mode 100644 6.graph/word-ladder/step3.cpp diff --git a/6.graph/word-ladder/memo.md b/6.graph/word-ladder/memo.md new file mode 100644 index 0000000..a19750c --- /dev/null +++ b/6.graph/word-ladder/memo.md @@ -0,0 +1,24 @@ +## step1 +- 今探索している単語の各文字を変え、その単語が `wordList` に存在したら探索。 + - これを `beginWord` から行っていく。 + +- 重要なのは、例えば `hot`→`hit`→`nit` と `hot`→`not`→`nit` は区別しなくていいこと。 + - つまり、 `hit` と `not` どちらの探索で `nit` を探索候補にし、単語リストから削除したとしても、全体の結果に影響しない。 + - ただしBFSでないとだめ。 + - DFSの場合、 `nit` から削除してしまう可能性があるから。 + +- 以下の処理がEBCDICでは通らない。 + ```cpp + for (char next_char = 'a'; next_char <= 'z'; ++next_char) + ``` + - EBCDICの場合、26文字ハードコーディングするか、0~2^8の中で `std::islower` が真のものを英小文字とする。 + +## step2 +- 他のコードを読む。 + +- 「変形する最小手順の探索(今回の問題)」と「次に探索できる文字の探索」は分けれる。 + - つまり、二重ループをループ二つに分けれる。 + - 参考:https://github.com/colorbox/leetcode/pull/34/ + +## step3 +- 補完なしで5分半ほどで実装。 diff --git a/6.graph/word-ladder/step1.cpp b/6.graph/word-ladder/step1.cpp new file mode 100644 index 0000000..13876a1 --- /dev/null +++ b/6.graph/word-ladder/step1.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +class Solution { +public: + int ladderLength(std::string beginWord, std::string endWord, std::vector& wordList) { + std::set candidate_words = std::set(wordList.begin(), wordList.end()); + std::queue words_to_explore; + words_to_explore.push(beginWord); + int ladder_length = 0; + while (!words_to_explore.empty()) { + int current_candidate_size = words_to_explore.size(); + ladder_length++; + for (int i = 0; i < current_candidate_size; ++i) { + std::string current_word = words_to_explore.front(); + words_to_explore.pop(); + if (current_word == endWord) { + return ladder_length; + } + for (int next_index = 0; next_index < current_word.size(); ++next_index) { + for (char next_char = 'a'; next_char <= 'z'; ++next_char) { + std::string next_word = current_word; + next_word[next_index] = next_char; + if (!candidate_words.contains(next_word)) { + continue; + } + candidate_words.erase(next_word); + words_to_explore.push(next_word); + } + } + } + } + return 0; // endWord が見つからなかった場合 + } +}; diff --git a/6.graph/word-ladder/step2.cpp b/6.graph/word-ladder/step2.cpp new file mode 100644 index 0000000..d09ca05 --- /dev/null +++ b/6.graph/word-ladder/step2.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +class Solution { +public: + int ladderLength(std::string beginWord, std::string endWord, std::vector& wordList) { + std::set candidate_words = std::set(wordList.begin(), wordList.end()); + if (!candidate_words.contains(endWord)) { + return 0; + } + candidate_words.insert(beginWord); + + auto adjacency_list = ComposeAdjacencyList(candidate_words); + std::queue words_to_explore; + words_to_explore.push(beginWord); + int ladder_length = 0; + while (!words_to_explore.empty()) { + ladder_length++; + int current_candidate_size = words_to_explore.size(); + for (int i = 0; i < current_candidate_size; ++i) { + std::string current_word = words_to_explore.front(); + words_to_explore.pop(); + for (std::string& next_word : adjacency_list[current_word]) { + if (next_word == endWord) { + return ladder_length + 1; + } + if (candidate_words.contains(next_word)) { + continue; + } + candidate_words.erase(current_word); + words_to_explore.push(next_word); + } + } + } + return 0; // endWord に到達しなかった場合 + } + +private: + std::map> ComposeAdjacencyLis(std::set& candidate_words) { + std::map> adjacency_list; + for (const std::string& word : candidate_words) { + for (int index = 0; index < word.size(); ++index) { + for (char next_letter = 'a'; next_letter <= 'z'; ++next_letter) { + std::string transformed_word = word; + transformed_word[index] = next_letter; + if (candidate_words.contains(transformed_word)) { + adjacency_list[word].push_back(transformed_word); + } + } + } + } + return adjacency_list; + } +}; diff --git a/6.graph/word-ladder/step3.cpp b/6.graph/word-ladder/step3.cpp new file mode 100644 index 0000000..9ad2bba --- /dev/null +++ b/6.graph/word-ladder/step3.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +class Solution { +public: + int ladderLength(std::string beginWord, std::string endWord, std::vector& wordList) { + std::set candidate_words = std::set(wordList.begin(), wordList.end()); + if (!candidate_words.contains(endWord)) { + return 0; + } + candidate_words.insert(beginWord); + + auto adjacency_list = ComposeAdjacencyList(candidate_words); + std::queue words_to_explore; + words_to_explore.push(beginWord); + int ladder_length = 0; + while (!words_to_explore.empty()) { + ladder_length++; + int current_candidate_size = words_to_explore.size(); + for (int i = 0; i < current_candidate_size; ++i) { + std::string current_word = words_to_explore.front(); + words_to_explore.pop(); + for (std::string& next_word : adjacency_list[current_word]) { + if (next_word == endWord) { + return ladder_length + 1; + } + if (candidate_words.contains(next_word)) { + continue; + } + candidate_words.erase(current_word); + words_to_explore.push(next_word); + } + } + } + return 0; + } + +private: + std::map> ComposeAdjacencyLis(std::set& candidate_words) { + std::map> adjacency_list; + for (const std::string& word : candidate_words) { + for (int index = 0; index < word.size(); ++index) { + for (char next_letter = 'a'; next_letter <= 'z'; ++next_letter) { + std::string transformed_word = word; + transformed_word[index] = next_letter; + if (candidate_words.contains(transformed_word)) { + adjacency_list[word].push_back(transformed_word); + } + } + } + } + return adjacency_list; + } +}; From 9d205f7a85624660741e678aabf5773f019c9230 Mon Sep 17 00:00:00 2001 From: Hurukawa2121 Date: Tue, 14 Jan 2025 21:52:46 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 6.graph/word-ladder/step2.cpp | 4 ++-- 6.graph/word-ladder/step3.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/6.graph/word-ladder/step2.cpp b/6.graph/word-ladder/step2.cpp index d09ca05..229769d 100644 --- a/6.graph/word-ladder/step2.cpp +++ b/6.graph/word-ladder/step2.cpp @@ -27,7 +27,7 @@ class Solution { if (next_word == endWord) { return ladder_length + 1; } - if (candidate_words.contains(next_word)) { + if (!candidate_words.contains(next_word)) { continue; } candidate_words.erase(current_word); @@ -39,7 +39,7 @@ class Solution { } private: - std::map> ComposeAdjacencyLis(std::set& candidate_words) { + std::map> ComposeAdjacencyList(std::set& candidate_words) { std::map> adjacency_list; for (const std::string& word : candidate_words) { for (int index = 0; index < word.size(); ++index) { diff --git a/6.graph/word-ladder/step3.cpp b/6.graph/word-ladder/step3.cpp index 9ad2bba..229769d 100644 --- a/6.graph/word-ladder/step3.cpp +++ b/6.graph/word-ladder/step3.cpp @@ -27,7 +27,7 @@ class Solution { if (next_word == endWord) { return ladder_length + 1; } - if (candidate_words.contains(next_word)) { + if (!candidate_words.contains(next_word)) { continue; } candidate_words.erase(current_word); @@ -35,11 +35,11 @@ class Solution { } } } - return 0; + return 0; // endWord に到達しなかった場合 } private: - std::map> ComposeAdjacencyLis(std::set& candidate_words) { + std::map> ComposeAdjacencyList(std::set& candidate_words) { std::map> adjacency_list; for (const std::string& word : candidate_words) { for (int index = 0; index < word.size(); ++index) { From ca96e7c4640a4635d2801594572ccf3c96734a54 Mon Sep 17 00:00:00 2001 From: Hurukawa2121 Date: Tue, 14 Jan 2025 22:43:43 +0900 Subject: [PATCH 3/3] =?UTF-8?q?step4=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 6.graph/word-ladder/step4.cpp | 74 +++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 6.graph/word-ladder/step4.cpp diff --git a/6.graph/word-ladder/step4.cpp b/6.graph/word-ladder/step4.cpp new file mode 100644 index 0000000..7c02388 --- /dev/null +++ b/6.graph/word-ladder/step4.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +class Solution { +public: + int ladderLength(std::string beginWord, std::string endWord, std::vector& wordList) { + std::set candidate_words = std::set(wordList.begin(), wordList.end()); + if (!candidate_words.contains(endWord)) { + return 0; + } + + candidate_words.insert(beginWord); + wordList.push_back(beginWord); + int begin_index = wordList.size() - 1; + std::queue indexes_to_explore; + indexes_to_explore.push(begin_index); + std::vector> adjacency_list = ComposeAdjacencyList(wordList); + + auto PushCandidateIndex = [&](int current_index) { + for (int candidate_index : adjacency_list[current_index]) { + if (wordList[candidate_index] == endWord) { + return false; + } + if (!candidate_words.contains(wordList[candidate_index])) { + continue; + } + candidate_words.erase(wordList[candidate_index]); + indexes_to_explore.push(candidate_index); + } + return true; + }; + + int ladder_length = 0; + while (!indexes_to_explore.empty()) { + ladder_length++; + int current_candidate_size = indexes_to_explore.size(); + for (int i = 0; i < current_candidate_size; ++i) { + int current_index = indexes_to_explore.front(); + indexes_to_explore.pop(); + if (!PushCandidateIndex(current_index)) { + return ladder_length + 1; + } + } + } + return 0; // endWord に到達しなかった場合 + } + +private: + int HammingDistance(std::string& word1, std::string& word2) { + int diff_count = 0; + for (int i = 0; i < word1.size(); ++i) { + if (word1[i] != word2[i]) { + diff_count++; + } + } + return diff_count; + } + + std::vector> ComposeAdjacencyList(std::vector& wordList) { + std::vector> adjacency_list(wordList.size()); + for (int i = 0; i < wordList.size(); ++i) { + for (int j = i + 1; j < wordList.size(); ++j) { + if (HammingDistance(wordList[i], wordList[j]) == 1) { + adjacency_list[i].push_back(j); + adjacency_list[j].push_back(i); + } + } + } + return adjacency_list; + } +};