This commit is contained in:
krahets
2026-08-18 03:09:26 +08:00
parent 0375f25b66
commit fde23f369e
158 changed files with 312 additions and 282 deletions
@@ -4700,7 +4700,7 @@
</ul>
<p>つまり、文字列 <span class="arithmatex">\(s\)</span> に対する各ラウンドの決定(編集操作)は、<span class="arithmatex">\(s\)</span><span class="arithmatex">\(t\)</span> における残りの未一致文字を変化させます。したがって、状態は現在 <span class="arithmatex">\(s\)</span><span class="arithmatex">\(t\)</span> で考えている第 <span class="arithmatex">\(i\)</span> と第 <span class="arithmatex">\(j\)</span> 文字とし、<span class="arithmatex">\([i, j]\)</span> と記します。</p>
<p>状態 <span class="arithmatex">\([i, j]\)</span> に対応する部分問題は、**<span class="arithmatex">\(s\)</span> の先頭 <span class="arithmatex">\(i\)</span> 文字を <span class="arithmatex">\(t\)</span> の先頭 <span class="arithmatex">\(j\)</span> 文字に変換するのに必要な最小編集回数**です。</p>
<p>これにより、サイズが <span class="arithmatex">\((i+1) \times (j+1)\)</span> の2次元 <span class="arithmatex">\(dp\)</span> テーブルが得られます。</p>
<p>これにより、サイズが <span class="arithmatex">\((n+1) \times (m+1)\)</span> の2次元 <span class="arithmatex">\(dp\)</span> テーブルが得られます。</p>
<p><strong>第2ステップ:最適部分構造を見つけ、状態遷移方程式を導く</strong></p>
<p>部分問題 <span class="arithmatex">\(dp[i, j]\)</span> を考えます。これに対応する2つの文字列の末尾文字は <span class="arithmatex">\(s[i-1]\)</span><span class="arithmatex">\(t[j-1]\)</span> であり、編集操作の違いに応じて下図の3つの場合に分けられます。</p>
<ol>
@@ -5709,7 +5709,7 @@ dp[i, c] = \max(dp[i-1, c], dp[i-1, c - wgt[i-1]] + val[i-1])
<p align="center"> 図 14-20 &nbsp; 0-1 ナップサック問題の動的計画法の過程 </p>
<h3 id="4">4. &nbsp; 空間最適化<a class="headerlink" href="#4" title="Permanent link">&para;</a></h3>
<p>各状態は直前の行の状態にしか依存しないため、2つの配列をローテーションして用いることで、空間計算量を <span class="arithmatex">\(O(n^2)\)</span> から <span class="arithmatex">\(O(n)\)</span> に削減できます。</p>
<p>各状態は直前の行の状態にしか依存しないため、2つの配列をローテーションして用いることで、空間計算量を <span class="arithmatex">\(O(n \times cap)\)</span> から <span class="arithmatex">\(O(cap)\)</span> に削減できます。</p>
<p>さらに考えると、1つの配列だけで空間最適化を実現できるでしょうか。観察すると、各状態は真上または左上のマスから遷移してきます。配列が1つしかないと仮定すると、<span class="arithmatex">\(i\)</span> 行目の走査を開始した時点では、その配列にはまだ <span class="arithmatex">\(i-1\)</span> 行目の状態が格納されています。</p>
<ul>
<li>順方向に走査すると、<span class="arithmatex">\(dp[i, j]\)</span> に到達した時点で、左上にある <span class="arithmatex">\(dp[i-1, 1]\)</span> ~ <span class="arithmatex">\(dp[i-1, j-1]\)</span> の値がすでに上書きされている可能性があり、正しい状態遷移結果を得られません。</li>
@@ -4651,7 +4651,7 @@
<p><strong>編集距離問題</strong></p>
<ul>
<li>編集距離(Levenshtein 距離)は 2 つの文字列間の類似度を測るために用いられ、ある文字列を別の文字列へ変換するための最小編集回数として定義されます。編集操作には追加、削除、置換が含まれます。</li>
<li>編集距離問題の状態は、<span class="arithmatex">\(s\)</span> の前 <span class="arithmatex">\(i\)</span> 文字を <span class="arithmatex">\(t\)</span> の前 <span class="arithmatex">\(j\)</span> 文字へ変更するのに必要な最小編集回数として定義されます。<span class="arithmatex">\(s[i] \ne t[j]\)</span> のときは、追加、削除、置換の 3 つの判断があり、それぞれに対応する残りの部分問題があります。これにより最適部分構造を見いだし、状態遷移方程式を構築できます。一方、<span class="arithmatex">\(s[i] = t[j]\)</span> のときは現在の文字を編集する必要はありません。</li>
<li>編集距離問題の状態は、<span class="arithmatex">\(s\)</span> の前 <span class="arithmatex">\(i\)</span> 文字を <span class="arithmatex">\(t\)</span> の前 <span class="arithmatex">\(j\)</span> 文字へ変更するのに必要な最小編集回数として定義されます。<span class="arithmatex">\(s[i-1] \ne t[j-1]\)</span> のときは、追加、削除、置換の 3 つの判断があり、それぞれに対応する残りの部分問題があります。これにより最適部分構造を見いだし、状態遷移方程式を構築できます。一方、<span class="arithmatex">\(s[i-1] = t[j-1]\)</span> のときは現在の文字を編集する必要はありません。</li>
<li>編集距離では、状態は真上、真左、左上の状態に依存します。そのため、空間最適化後は順方向でも逆方向でも正しく状態遷移できません。そこで、変数を 1 つ用いて左上の状態を一時保存し、完全ナップサック問題と等価な形へ変換することで、空間最適化後に順方向走査を行えるようにします。</li>
</ul>