Skip to content

Create WordLadder.md#22

Open
irohafternoon wants to merge 1 commit into
mainfrom
127.-Word-Ladder
Open

Create WordLadder.md#22
irohafternoon wants to merge 1 commit into
mainfrom
127.-Word-Ladder

Conversation

@irohafternoon
Copy link
Copy Markdown
Owner

This Problem
Word Ladder
Next Problem
Maximum Depth of Binary Tree


#### 2周目の宿題
- ダイクストラを適用する(BFSとの違いを考えながら)
- pythonのジェネレータやyieldを理解する
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まあ、このへんはオプショナルかもしれません。

C++20 から coroutine が、C++23 から Generator があるらしいです。
https://discord.com/channels/1084280443945353267/1247673286503039020/1263415894030290945

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。合わせてこちらもとりあえず見てみようと思います

public:
int ladderLength(std::string beginWord, string endWord,
const std::vector<std::string> &wordList) {
std::set<std::string> available_words(wordList.begin(), wordList.end());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下のようにすることでcopyが免れます (string ownership は元のwordList)

Suggested change
std::set<std::string> available_words(wordList.begin(), wordList.end());
std::set<std::string_view> available_words(wordList.begin(), wordList.end());

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
string_viewを使用することで、setの各要素は、実際には元のwordListを参照するようにできるのですね。
ただ、元のwordListの要素が消去されたりする場合は、注意が必要ということですね
また、string_viewの特徴として、所有権は参照元が持つことを理解しました。

std::set<std::string> seen_words;
seen_words.insert(beginWord);
std::queue<std::pair<std::string, int>> word_to_visit;
word_to_visit.push({beginWord, 1});
Copy link
Copy Markdown

@austyhooong austyhooong Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下 word_to_visit.pushも同じく

Suggested change
word_to_visit.push({beginWord, 1});
word_to_visit.emplace(beginWord, 1);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、stringはオブジェクトのサイズが大きくなるため、emplaceにすべきでした。

}

private:
bool is_adjacent_word(const std::string& word1, const std::string& word2) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool is_adjacent_word(const std::string& word1, const std::string& word2) {
bool is_adjacent_word(const std::string& word1, const std::string& word2) const {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@austyhooong
ありがとうございます。こちらは、「メンバ変数にアクセスしないような関数でも、メンバ変数を変更しないことを明示するためにconstをつけるべき」という意味合いでしょうか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい、またもし class instance 自身がconst objectの場合 non const member function は呼べなくなります。そのためnon modifying member functionをconstにすることは無難かと思います

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class A{
public:
    A(int x) : x_(x){}
    int f() {  //not int f() const {}
        return x_;
    }
private:
    int x_;
};

int main() {
    A a(5);
    cout<<a.f()<<endl; //5
    const A b(5);
    cout<<b.f()<<endl; //error
}

ありがとうございます。こちらは知らず、大変参考になります。
実験して、constがついたオブジェクトだとcon constな関数を呼べないことを確認しました

int diff_count = 0;
for (int i = 0; i < word1.size(); i++) {
if (word1[i] != word2[i]) {
diff_count ++;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
diff_count ++;
++diff_count;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@austyhooong ありがとうございます。
google style guideにもそのように記載がありました
https://google.github.io/styleguide/cppguide.html#Preincrement_and_Predecrement

 Use the prefix form (++i) of the increment and decrement operators unless you need postfix semantics.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

インクリメントを前置するか後置するかで、Google が前置を好むのは理屈として複雑です。

int のようなものの場合、前置でも後置でも特に差はありません。しかし、イテレーターのようなクラスである場合は、後置はコピーを作らなくてはいけません。最適化によって消えるかもしれませんが消える保証はありません。そこでパフォーマンスが重要でクラスの場合には前置のほうがよい場合があります。

そこまで考えたうえで、そうでない場合に後置のほうがいいことはあまりないので、特段の事情がない限りは前置で統一しましょうというルールになっています。

ここまでの理屈を前提にしたうえで、私が、ここまでしつこく書いているのは、「前置を好む」は常識ではないにしても「上の理屈が通じる」くらいが常識なので、「過程を飛ばして結論だけ覚えていると常識がない」と見えるからです。逆に「結論を知らなくても過程が通じればよほど常識がある」と判断されるでしょう。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
仰る通り、「とりあえず結論だけ覚えておこう」という常識からかけ離れた態度でした。反省します。
今後は、「〇〇の方が良い」と自分で結論付けるなら、その理由を調べたり聞いたりして理解した上でにする、もしその場で理解できない場合は、「〇〇の方が良いらしいが、自分は調べてもはっきりとは分からなかった。」というスタンスを取るように気をつけたいと思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そうですね。分からないといえるのは大事です。

https://en.cppreference.com/w/cpp/iterator/iterator

        iterator& operator++() { num = TO >= FROM ? num + 1: num - 1; return *this; }
        iterator operator++(int) { iterator retval = *this; ++(*this); return retval; }

典型的な前置と後置のインクリメントは、このようになっています。前置はリファレンスが返るのに対して、後置はコピーが返ります。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
後置はインクリメントされる前を返さないといけないため、インクリメント前のものをコピーをしており、前置はインクリメントされた後を返すので、自身への参照を返していると理解しました。

word_list.insert(beginWord);
for (const auto& original_word : word_list) {
for (int index = 0; index < original_word.size(); index++) {
for (int diff = 0; diff < 26; diff++) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (chat ch = 'a'; ch <= 'z'; ++ch) { で回したほうが素直だと思いました。

STEP2 のように kLowerCase で回すのもよいと思いました。

なお、英小文字アルファベットが文字コード上で連続しているかどうかについて、過去のレビューコメントを調べることをお勧めいたします。 EBCDIC あたりで検索してみてください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます
philip82148/leetcode-swejp#5 (comment)
こちらのコメントにあるのを確認しました
https://ja.wikipedia.org/wiki/EBCDIC
IBM系のコンピュータで利用されている文字コードと理解しました。
環境によって文字コードがいろいろあるということを覚えておこうと思います

std::vector<std::string> wordList) {
const size_t word_length = beginWord.size();
wordList.push_back(beginWord);
std::map<std::string, std::vector<std::pair<std::string, std::string>>>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key として、 "abc?efg" のような文字列を使ってもよいと思います。

Copy link
Copy Markdown
Owner Author

@irohafternoon irohafternoon Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます、分離する切れ目部分を、共通の文字でおくことで、タプルに分割する必要がなくなると理解しました。このような発想はなく大変参考になります。
以下のように実際に変更可能なことを確認しました。

class Solution {
public:
    int ladderLength(std::string beginWord, std::string endWord,
                     std::vector<std::string> wordList) {
        const size_t word_length = beginWord.size();
        wordList.push_back(beginWord);
        std::map<std::string, std::vector<std::string>>
                 word_to_keys;
        std::map<std::string, std::vector<std::string>>
                 key_to_words;
        bool is_valid_input = false;
        for (const auto& word : wordList) {
            if (word == endWord) {
                is_valid_input = true;
            } 
            for (int i = 0; i < word_length; i++) {
                auto key = word.substr(0, i) + "?" + word.substr(i + 1, word_length - i - 1);
                word_to_keys[word].push_back(key);
                key_to_words[key].push_back(word);
            }
        }
        if (!is_valid_input) {
            return 0;
        }
   // 以下略

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants