????????這次分享一道關于動態規劃的leetcode,單詞拆分。
單詞拆分
給你一個字符串 s 和一個字符串列表 wordDict 作為字典。如果可以利用字典中出現的一個或多個單詞拼接出 s 則返回 true。注意:不要求字典中出現的單詞全部都使用,并且字典中的單詞可以重復使用。
示例:
輸入: s = “leetcode”, wordDict = [“leet”, “code”]
輸出: true
解釋: 返回 true 因為 “leetcode” 可以由 “leet” 和 “code” 拼接成。
class Solution {public boolean wordBreak(String s, List<String> wordDict) {boolean[] dp = new boolean[s.length() + 1];dp[0] = true;for(int i = 0;i <= s.length();i++){for(int j = 0;j <= i;j++){if(dp[j] && wordDict.contains(s.substring(j, i))){dp[i] = true;}}}return dp[s.length()];}
}
????????先看遞推公式,舉個例子,字符串為"appendapple",字符串列表為[“app”,“end”,“apple”],如果已經知道字符串append可以由字符串列表中單詞構成,那么只要判斷apple是否包含在字符串列表中即可,因此可以有遞推公式,dp[j] && wordDict.contains(s.substring(j, i))
。解釋一下,dp[j]表示下標0到下標j - 1這段字符串可以由字符串列表單詞構成(可能有多個),剩下的只需要判斷下標j到i的字符串是否出現在字符串列表中(下標j到i為一個字符串,即一個單詞)。因此dp[i]的含義為以下標i - 1結尾的字符串可以(不可以)由給定的字符串列表中的單詞構成,注意是i - 1,dp[i]為true,可以,dp[i]為false,則不可以。為什么是i - 1,這是由于substring方法截取字符串時區間是左閉右開的。最后是創建dp,長度為字符串長度加1,數組初始化,僅需將dp[0]初始化為true即可,為什么等會說明。
????????以上述示例說明代碼的執行流程,首先初始化dp[0] = true,接著進入for循環,當i = 0,j = 0時,substring(0, 0),為空。當i = 1,j = 0時,substring(0, 1),為l,不再字符串列表中,j++,substring(1, 1)為空。當i = 2,j = 0,substring(0, 2)為le,j++,substring(1, 2)為e,j++,substring(2,2)為空,均不在字符串列表中。當i = 3,j等于0,substring(0, 3)為lee,j++,substring(1, 3)為ee,substring(2,3)為e,substring(3,3)為空,均不在字符串列表中。當i = 4,j等于0,substring(0, 4)為leet,出現在字符串列表中,wordDict.contains(s.substring(j, i)結果為true,dp[j]即為dp[0],這時就涉及到dp[0]的初始化,如果初始化為false,導致dp[4]無法初始化,因此dp[0]初始化為true,因此dp[4] = true,可以判斷了leet出現在字符串列表中,雖然是dp[4],但是僅判斷了下標0到3,并沒有包括下標4。j++,substring(1, 4)為eet,substring(2, 4)為et,substring(3, 4)為t,substring(4,4)為空,均不在字符串列表中。接著當i = 5,截取的字符串為leetc,eetc,etc,tc,c和空。接著當i = 6是,截取的字符串為leetco,eetco,etco,tco,co,o和空,接著i = 7,截取的字符串為leetcod,eetcod,etcod,tcod,cod,od,d和空,均不存在于字符串列表中。當i = 8,j = 0,截取字符串為leetcode,eetcode,etcode,tcode,,均不存在于字符串列表中。當i = 8,j = 4,substring(4, 8)截取的字符串為code,出現在字符串列表中,并且dp[4]為true,因此dp[8]為true,之后截取的字符串為ode,de,e和空,,均不存在于字符串列表中。內外層循環遍歷結束,返回dp[字符串長度 + 1],leetcode長度為7,返回dp[8]為true,表示的含義是下標0-7的字符串可以由字符串列表中的單詞構成。