本文正在參加「Java主題月 - Java 刷題打卡」,詳情查看活動連接java
給定一個非空字符串 s 和一個包含非空單詞的列表 wordDict,斷定 s 是否能夠被空格拆分爲一個或多個在字典中出現的單詞。算法
- 說明:
- 拆分時能夠重複使用字典中的單詞。
- 你能夠假設字典中沒有重複的單詞。
- 示例:
- 輸入: s = "leetcode", wordDict = ["leet", "code"]
- 輸出: true
- 解釋: 返回 true 由於 "leetcode" 能夠被拆分紅 "leet code"。
若是字符串s能被拆分爲多個單詞,則必定存在j使得 s.substring(j)
和 s.substring(j,slen+1)
兩個子串都能被拆分爲多個單詞。同理,兩個子串也存在於字符串s相同的性質。則咱們能夠從0遍歷 s.length()
,記錄每一個位置以前的子串是否可以被拆分爲多個單詞。數組
dp[i]: s.substring(i)
可否被拆分爲單詞。注:dp[i] 記錄的應該是字符串下標爲 i-1 的位置能否能拆分爲多個單詞的狀況。markdown
對於dp[i],若是 s.substring(i)
能被拆分爲多個單詞,則必定存在j使得 s.substring(j)
子串能被拆分爲多個單詞,以及 s.substring(j,i)
子串爲一個單詞。因此有,dp[i] = dp[j] && wordDict.contains(s.substring(j,i))
。oop
dp[0]表明的是空子串的狀況,題目中指出,給定一個非空字符串,因此空子串在本題中應該是無心義的。可是,dp[i]須要依靠dp[j]肯定,若是dp[0]爲false,則後面dp[i]是否爲true都將無心義,因此dp[0]必須爲true。post
本題使用兩層循環遍歷,外層循環天然是從0遍歷到s.length()。對於內層,從i遍歷到1能明顯減小循環次數。spa
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordSet = new HashSet<>(wordDict);
int sLength = s.length();
boolean[] dp = new boolean[sLength+1];
dp[0] = true;
for(int i = 1; i <= sLength; i++){
for (int j = i; j >= 0; j--){
if(dp[j] && wordSet.contains(s.substring(j,i))){
dp[i] = true;
break;
}
}
}
return dp[sLength];
}
}
複製代碼
140.單詞拆分IIcode
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
Set<String> wordSet = new HashSet<>(wordDict);
int len = s.length();
// 第 1 步:動態規劃計算是否有解
boolean[] dp = new boolean[len + 1];
dp[0] = true;
for (int right = 1; right <= len; right++) {
// 從後向前遍歷是更快的
for (int left = right - 1; left >= 0; left--) {
if (wordSet.contains(s.substring(left, right)) && dp[left]) {
dp[right] = true;
break;
}
}
}
// 第 2 步:回溯算法搜索全部符合條件的解
List<String> res = new ArrayList<>();
if (dp[len]) {
Deque<String> path = new ArrayDeque<>();
dfs(s, len, wordSet, dp, path, res);
return res;
}
return res;
}
/** * s[0:len) 若是能夠拆分紅 wordSet 中的單詞,把遞歸求解的結果加入 res 中 * * @param s * @param len 長度爲 len 的 s 的前綴子串 * @param wordSet 單詞集合,已經加入哈希表 * @param dp 預處理獲得的 dp 數組 * @param path 從葉子結點到根結點的路徑 * @param res 保存全部結果的變量 */
private void dfs(String s, int len, Set<String> wordSet, boolean[] dp, Deque<String> path, List<String> res) {
if (len == 0) {
res.add(String.join(" ",path));
return;
}
// 能夠拆分的左邊界從 len - 1 依次枚舉到 0
for (int i = len - 1; i >= 0; i--) {
String suffix = s.substring(i, len);
if (wordSet.contains(suffix) && dp[i]) {
path.addFirst(suffix);
dfs(s, i, wordSet, dp, path, res);
path.removeFirst();
}
}
}
}
複製代碼