139. Word Break#37
Conversation
| class Solution { | ||
| public: | ||
| bool FindWordRecursively(string s, vector<string>& wordDict, unordered_map<string, bool>& memo) { | ||
| if (s.size() == 0) { |
There was a problem hiding this comment.
if (s.empty()) {のほうがシンプルだと思います。
https://cpprefjp.github.io/reference/string/basic_string/empty.html
There was a problem hiding this comment.
ありがとうございます。そちらの方がシンプルそうです。
| return true; | ||
| } | ||
|
|
||
| if (memo.count(s)) { |
There was a problem hiding this comment.
C++20 以降なら std::unordered_map::contains() を使うことをお勧めいたします。
https://cpprefjp.github.io/reference/unordered_map/unordered_map/contains.html
There was a problem hiding this comment.
ありがとうございます。こちらのメンバ関数は知りませんでした。
There was a problem hiding this comment.
こちらですが過去のPRで複数回同じ指摘があったようです。同じ指摘を受けないように、次回以降のPRで反映させるとより効果的な学習になると思います。
| @@ -0,0 +1,28 @@ | |||
| class Solution { | |||
| public: | |||
| bool FindWordRecursively(string s, vector<string>& wordDict, unordered_map<string, bool>& memo) { | |||
There was a problem hiding this comment.
memo という変数名に、あまり情報がないように思いました。ある接頭辞が breakable かどうかを表す map ですので、 prefix_to_breakable はいかがでしょうか?
There was a problem hiding this comment.
ありがとうございます。そちらの名前にしようと思います。
| @@ -0,0 +1,28 @@ | |||
| class Solution { | |||
| public: | |||
| bool FindWordRecursively(string s, vector<string>& wordDict, unordered_map<string, bool>& memo) { | |||
There was a problem hiding this comment.
string を値渡しすると、毎回コピーが発生します。 const 参照で渡すことをお勧めいたします。
| @@ -0,0 +1,28 @@ | |||
| class Solution { | |||
| public: | |||
| bool FindWordRecursively(string s, vector<string>& wordDict, unordered_map<string, bool>& memo) { | |||
There was a problem hiding this comment.
unordered_map<string, bool> より、 std::vector として、何文字目以降の接頭辞は breakable かを保持するようにしたほうが、ハッシュの計算が省略でき、軽くなると思います。未調査・breakable・unbreakable の 3 つの値を格納することになると思います。
There was a problem hiding this comment.
ありがとうございます!こちらの実装alternative-solution.cppで追加してみました。
| @@ -0,0 +1,11 @@ | |||
| ### step1 | |||
|
|
|||
| 再帰的に辞書からワードを探す形で実装。TLEしたのでメモ化する部分だけChatGPTに書いてもらった。 | |||
| @@ -0,0 +1,27 @@ | |||
| class Solution { | |||
| public: | |||
There was a problem hiding this comment.
アクセス修飾子を意識して使っていますか?このコードでは、どう変更するのが適切そうでしょうか?
There was a problem hiding this comment.
あまり意識していませんでした。このコードでは再帰関数は公開しなくていいと思うのでprivateにするのがよいでしょうか?
class Solution {
private:
bool FindWordRecursively(string s, const vector<string>& wordDict, unordered_map<string, bool>& memo) {
if (s.size() == 0) {
return true;
}
if (memo.count(s)) {
return memo[s];
}
for (int i = 0; i < wordDict.size(); i++) {
if (s.starts_with(wordDict[i])) {
if (FindWordRecursively(s.substr(wordDict[i].size()), wordDict, memo)) {
return memo[s] = true;
}
}
}
return memo[s] = false;
}
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_map<string, bool> memo;
return FindWordRecursively(s, wordDict, memo);
}
};
| return memo[s]; | ||
| } | ||
|
|
||
| int size = wordDict.size(); |
There was a problem hiding this comment.
wordDict.size()のままで良いと思います.
sizeは1箇所しか用いず,また可読性の面でも大差ないと思うので.
There was a problem hiding this comment.
そうですね、特に変数化しなくてよかったかもしれないです。
| int size = wordDict.size(); | ||
| for (int i = 0; i < size; i++) { | ||
| if (s.starts_with(wordDict[i])) { | ||
| if (FindWordRecursively(s.substr(wordDict[i].size()), wordDict, memo)) { |
There was a problem hiding this comment.
string型に対するsubstr関数は都度コピーを生成することになり遅いので,string_viewに変換して扱ってはいかがでしょうか.
There was a problem hiding this comment.
ありがとうございます!string_viewは知りませんでした。
| for (int i = 0; i < size; i++) { | ||
| if (s.starts_with(wordDict[i])) { | ||
| if (FindWordRecursively(s.substr(wordDict[i].size()), wordDict, memo)) { | ||
| return memo[s] = true; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
実際には最悪計算量よりも平均的な計算量のほうが重要でしょうが,今回はそれがわからないので最悪計算量で考えると,今回のケースでは
wordDictのサイズが最大で2000sのサイズは最大20
ですからwordDictを操作するよりもwordDictをsetかunordered_setに変換してsの部分文字列を操作する方が最悪計算量が小さく抑えられます.
この問題:https://leetcode.com/problems/word-break/description/
次に解く問題:https://leetcode.com/problems/coin-change/description/