Conversation
naoto-iwase
left a comment
There was a problem hiding this comment.
お疲れさまです。
(いつもなんですが)思考過程のログが非常に丁寧でとても良いなぁと感じました。こちらまで勉強になります。
DPを中心にコメントさせていただきました。
| if !segmentable_words[i] { | ||
| break; | ||
| } |
There was a problem hiding this comment.
これは、word_dictのループに入る前にチェックできることであり、実際そうした方がパフォーマンスで有利だと思います。
|
|
||
| if peeked_word != word { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
あえてif-continueを選ばれたということだと思いますが、その後に行うsegmentable_words[i + word.len()] = trueはif節の不成立に密接に関連しているので、単に、
if peeked_word == word {
segmentable_words[i + word.len()] = true;
}の方が素直に感じます。これはif !segmentable_words[i] { break; }を外に出すことを前提にした提案ですね。
| /* | ||
| 所感 | ||
| - DP配列を先頭から末尾に向けて走査する方法だと手が止まって解けなかった。時間切れなのでGPT-5.1に聞く | ||
| - 空文字をtrueとしてdp[0]をtrueとする。<- 直感に反するので腹落ちしない |
There was a problem hiding this comment.
この実装においてsegmentable_words[i]の真偽は、「sのi番目より左がword_dictで分割可能」という意味になりますね。
この実装を自然言語で端的に述べると、「segmentable_words[i]がtrueかつs.get(i..i + word.len())のスライスがword_dictにあるならば、segmentable_words[i + word.len()]もtrue」ですよね。i + word.len()番目はスライスの右隣であることがやや直感的でないと感じるかもしれません。
そしてこのとき、segmentable_words[0]は、「sの0番目より左がword_dictで分割可能か」なので、「空文字列はword_dictで分割可能か」であり、それはtrueとなりますね。
|
|
||
| *segmentable_words.last().unwrap() | ||
| } | ||
| } |
There was a problem hiding this comment.
これはいわゆる「配るDP」ですね。自分のソリューションを見返すと「貰うDP」で書いてました。
一応DPは実装方法がこの2つあることは知っておくと良い気がします。ちなみにこれらは競プロ用語らしいです。
「配るDP」は、たぶん競技プログラミング同好会用語です。探したら秋葉拓哉のスライドが出てきました。
https://www.slideshare.net/slideshow/ss-3578511/3578511#58
https://discord.com/channels/1084280443945353267/1226508154833993788/1307700721138929759
|
|
||
| for i in 0..s.len() { | ||
| for word in &word_dict { | ||
| let Some(peeked_word) = s.get(i..i + word.len()) else { |
There was a problem hiding this comment.
i + word.len()はこの下でも出てくるのでendなどと名付けておくと読みやすいかもしれません。
| for i in 0..s.len() { | ||
| for word in &word_dict { | ||
| let Some(peeked_word) = s.get(i..i + word.len()) else { | ||
| continue; | ||
| }; | ||
|
|
||
| if peeked_word != word { | ||
| continue; | ||
| } | ||
|
|
||
| if !segmentable_words[i] { | ||
| break; | ||
| } | ||
|
|
||
| segmentable_words[i + word.len()] = true; | ||
| } | ||
| } |
There was a problem hiding this comment.
以上のレビューコメントを反映した実装例を書いてみますね。
for i in 0..s.len() {
if !segmentable_words[i] {
continue;
}
for word in &word_dict {
let end = i + word.len();
if end > s.len() {
continue;
}
if &s[i..end] == word {
segmentable_words[end] = true;
}
}
}let Some(peeked_word) = s.get(i..i + word.len()) else { continue; }のところは、i + word.len() <= s.len()と言い換えられます。DPもi + word.len()で登録していることから、この二重ループ内全体をend = i + word.len()を主役に据えて書くことで一貫性が高まると個人的に感じたのでそのように変更してみました。
問題: 139. Word Break
次に解く問題: 322. Coin Change
ファイルの構成:
./src/bin/<各ステップ>.rs