一.明確dp陣列的含義
構造一維dp[i]、二維dp[i][j],明確其代表了什麼狀態,一般與題目的目的有關。如解決萬用字元問題,就定義boolean dp表示s[i-1]與p[j-1]的匹配成功與否;解決最長子陣列問題,就定義dp表示a[i-1]與b[j-1]的最長字尾公共陣列長度;解決最長子序列問題,就定義dp表示a[i-1]與b[j-1]的最長公共序列長度。
二.定義base case基本值
dp[0][j]與dp[i][0]表示什麼情況,有時預設為0,有的情況需要初始化
如萬用字元問題dp[0][j]表示主串為0模式串不為0的情況下,*可以使其為true,dp[i][j]表示主串不為0模式串為0,一律為false,而dp[0][0]=true
而在最長公共子陣列/子串行問題中預設初始都為0
三.尋找狀態轉移方程/繼承關係
關鍵一步:已知上一步的狀態,如何求當前狀態值。
在萬用字元問題中分情況討論,a與a/b ,a與?,a與*,』 '與*四種情況
在子陣列問題中,當前字元相等則count+1,否則為0
在子串行問題中,當前字元相等則count+1,否則繼承a[i-1]b[j]或a[i]b[j-1]的較大值。
最後返回最終狀態即可。
動態規劃可以解決的問題:
型別1:兩個字串/陣列之間的某種關係,如最長公共子串,最長公共子序
列,兩字串匹配…
型別2:路徑規劃方案 leetcode 66 路徑之和
leetcode 44 字串的通配
* :任意長度字串(可為0)
?:單個字元
【分析思路】
假設主串a的長度為n,模式串b的長度為m,比較的情況有如下幾種:
1.如果b的位置上為普通字元,則比較兩個字元是否匹配,相等則繼續向前比較 a[n-2]與b[m-2]
2.如果b位置上為?,則跳過比較a[n-1],向前比較a[n-2]與b[m-2]
3.如果b位置上為*,的作用分為2種:
①匹配0個a字元,下一步比較a[n-1]與b[m-2],即b向前走一步,a不動
②匹配多個a字元,下一步比較a[n-2]與b[m-1],即a向前走一步,b不動
【轉移方程】
f[i][j]代表a的前i個和b的前j個字元能否匹配
·對於情況1和2,合併 f[i][j] = f[i-1][j-1]
·對於情況3:
①a不動,b動,跳過b的,向前繼承:f[i][j] = f[i][j-1]
②a動,b不動,*的作用還可以保留在下一次使用,向前繼承:
f[i][j] = f[i-1][j],這裡的b[j]還是 ,仍可匹配多次
邊界情況:
① dp[0][0]=true,即當字串s和模式p均為空時,匹配成功;
② dp[i][0]=false,即空模式串無法匹配字串
③ dp[0][j],看p前面有幾個就匹配多少空字元,**3:true,true,false
【**】
// 動態規劃 dp[i][j]表示s(i)和p(j)前的字串的能否匹配上
public
static
boolean
ismatch
(string s, string p)
else
}for
(int i =
1; i <= m;
++i)
else
if(p.
charat
(j -1)
=='?'
|| s.
charat
(i -1)
== p.
charat
(j -1)
)}}return dp[m]
[n];
}
leetcode 718 最長公共/重複子陣列
給出兩個整數陣列a和b,返回兩個陣列中公共的、最長的子陣列
[暴力搜尋]
ans = 0
for i in [0 .. a.length - 1]
for j in [0 .. b.length - 1]
k = 0
while (a[i+k] == b[j+k]) do # and i+k < a.length etc.
k += 1
end while
ans = max(ans, k)
end for
end for
如何優化? 暴力搜尋進行了重複比較,為了只比較一次,用動態規劃記錄當前位置的兩個字串最長公共字尾陣列,進行狀態轉移
dp[i][j]表示a[i-1]前的陣列中與b[j-1]前的陣列中的最長公共長度(必須包含末尾數字,即以a[i-1],b[j-1]為字尾的最長子陣列)
[狀態轉移方程]
若a[i-1]==b[j-1],則dp[i][j] = dp[i-1][j-1] + 1;
否則dp[i][j] = 0;
//leetcode 718動態規劃解決最長公共子陣列
public
static
intmaxcommonsubarray
(int
a,int[
] b)
max = math.
max(max,dp[i]
[j]);}
}return max;
}
leetcode 1143 最長公共/重複子序列
兩個字串的最長公共子串行 序列是指:「ace」 是 「abcde」 的子串行,即在不改變順序的情況下可以忽略某些字串
因為子串行型別的問題,窮舉出所有可能的結果都不容易,而動態規劃演算法做的就是窮舉 + 剪枝,它倆天生一對兒。所以可以說只要涉及子串行問題,十有**都需要動態規劃來解決,往這方面考慮就對了。
[狀態轉移]
思路:理清楚當前狀態如何繼承上乙個狀態
當比較到dp[i][j]位置時,對於a[i-1]與b[j-1],
若a[i-1]=b[j-1],則繼承dp[i-1][j-1] + 1
若a[i-1]!=b[j-1],則取a[i-2]&b[j-1]或a[i-1]&b[j-2]比較結果的較大值繼承:因為當前不等,就取前面子串行的最大值繼承max(dp[i-1][j],dp[i][j-1])。
【**】
public
intlongestcommonsubsequence
(string text1, string text2)
else}}
return dp[len1]
[len2]
;}
可見最長公共子陣列和最長公共子串行的思路一致,細節不同在於寫狀態轉移方程時如何繼承上乙個狀態值。
子陣列的問題在於若比較的字元不同,則公共長度為0(以其為字尾)
子串行的問題在於若比較的字元不同,則公共長度繼承之前子串行的最大值
專題學習 LCT
等下發樹剖的學習筆記 lct link cut tree 是解決一類動態樹問題的資料結構 主要是給乙個有根樹的森林,然後有動態插入邊,刪除邊,詢問等操作 保證時刻是乙個森林 lct維護子樹資訊比較麻煩,這裡暫時不提 一 實邊和虛邊 lct 會將兒子劃分為虛 實兩種兒子,相應的邊稱為虛邊或實邊,且任意...
微服務專題學習01
springboot結合前端有很多種方法,比如在static裡面直接加入css或js,又或者引入webjars,以jar包的形式加入專案 webjars形式 pom 檔案 引用bootstrap org.webjars groupid bootstrap artifactid 3.3.7 1 ver...
動態規劃專題
多階段過程轉化為一系列單階段問題,利用各階段之間的關係,逐個求解,創立了解決這類過程優化問題的新方法 動態規劃 個人的理解 就是處於當前決策時要依賴前面的已知情況,將看似 連續無統一標準解決方案 的問題分割成多個 可以商量的 的決策過程。商量就是依靠已知的情況覺得未知 那麼什麼問題才可以用到動態規劃...