一、Problem
給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,判定 s 是否可以被空格拆分爲一個或多個在字典中出現的單詞。
說明:
- 拆分時可以重複使用字典中的單詞。
- 你可以假設字典中沒有重複的單詞。
輸入: s = "applepenapple", wordDict = ["apple", "pen"]
輸出: true
解釋: 返回 true 因爲 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重複使用字典中的單詞。
輸入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
輸出: false
二、Solution
方法一:dp
- 定義狀態:
- 表示字符串 的前 個字符能否拆分成一個或多個在字典中出現的單詞
- 思考初始化:
- ,我是試了下空串才知道的爲 true,可能是…
- 思考狀態轉移方程:
- 如果存在 ,則有 表示如果子串 能在字典中找到的前提條件是,子串 在字典中,且 也在字典中
- 思考輸出:
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> st = new HashSet<>(wordDict);
int n = s.length();
boolean f[] = new boolean[n+1];
f[0] = true;
for (int r = 1; r <= n; r++)
for (int l = 0; l < r; l++) {
if (f[l] && st.contains(s.substring(l, r))) {
f[r] = true;
break;
}
}
return f[n];
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,
方法二:優化
- 第一:如果 wordDict 中的單詞長度都比 1 大,那麼右邊界 從 1 開始也就變得有點雞肋了,所以 可從 wordDict 的最短單詞長度 mi 開始
- 第二:如果 很大,而字 wordDict 中的單詞最大長度都比 要短,那麼此時 這個子串也肯定在 wordDict 找不到,所以 l 可以從最大 wordDict 中的最大單詞長度的其實位置開始
基於以上兩個問題,我們可把代碼改爲:
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> st = new HashSet<>(wordDict);
int n = s.length(), mi = 0, mx = -1;
boolean f[] = new boolean[n+1];
f[0] = true;
for (String w : st) {
if (w.length() > mx) mx = w.length();
if (mi == 0 || mi > w.length()) mi = w.length();
}
for (int r = mi; r <= n; r++)
for (int l = Math.max(0, r-mx); l < r; l++) {
if (f[l] && st.contains(s.substring(l, r))) {
f[r] = true;
break;
}
}
return f[n];
}
}
複雜度分析
- 時間複雜度:,
- 空間複雜度:,