140. Word Break II
阿新 • • 發佈:2021-01-07
技術標籤:DFSDynamic Programmingleetcode
Lc-140
categories: [LeetCode]
tags: [Dynamic Programming, DFS, hard]
140. Word Break II
題目大意:
給定一個非空字串和一個字典包含一個非空的 list of non-empty words
在字串中加上空格使字串變成一個句子,使用空格分開的每個單詞都能在字典中找到
返回符合條件的所有可能的組合
注意:
同一個單詞在字典中可以被使用多次,並且字典中沒有重複的單詞
解題思路:
首先這道題是一道DP和DFS結合的題,我們將字典中的所有單詞放入一個HashSet中保證每一個單詞都是不同的 同時我們建立一個boolean型別且長度為s.length()+1的陣列dp表示 長度為i的字串s的字首可以拆分成字典中的單詞 dp[0]為true,因為代表長度為0的字串 接下來我們利用兩個 for 迴圈去遍歷不同長度的字首字串是否可以在hashSet中找到 如果字首字串substring(left,right)可以在HashSet中找到,且left之前的字串也為true, 則dp[right]為true,並break,因為無需判斷其他的了 接下來我們初始化一個list<string> ans儲存最終結果 如果dp[len]為true表示長度為len的字串可以再字典中找到對應的單詞 建立一個path去儲存字典中能組成的單詞, 然後用dfs進行搜尋 在dfs搜尋中, 如果當前字串長度len為0,則將現有的path放入到ans當中, 並用String.join(" ", path)將path用空格分開 如果長度不為空, 我們用for loop從最後一個字母開始迴圈, 用substring(i, len)取不同長度的字串作為字尾, 如果當前所取字尾在wordSet中,並且dp[i]為true,說明i之前的字串也可以在wordSet中找到, 我們將此時的字尾字串加在path中,然後進行dfs,此時dfs中的len 為 i 然後我們再用removeFirst將之前加進去的字尾刪掉 這樣以當前長度為字尾的字串和剩下的長度的字串就組成一個sentence的組合 然後進行下一個i-- 的for 迴圈直到i=0
注意:
class Gfg1 { public static void main(String args[]) { // delimiter is "<" and elements are "Four", "Five", "Six", "Seven" String gfg1 = String.join(" < ", "Four", "Five", "Six", "Seven"); System.out.println(gfg1); } } // Output: Four < Five < Six < Seven
複雜度:
Time Coplexity:
Space Complexity:
Code示例:
class Solution { public List<String> wordBreak(String s, List<String> wordDict) { // 為了快速判斷一個單詞是否在單詞集合中,需要將它們加入雜湊表 Set<String> wordSet = new HashSet<>(wordDict); int len = s.length(); // 第 1 步:動態規劃計算是否有解 // dp[i] 表示「長度」為 i 的 s 字首子串可以拆分成 wordDict 中的單詞 // 長度包括 0 ,因此狀態陣列的長度為 len + 1 boolean[] dp = new boolean[len + 1]; // 0 這個值需要被後面的狀態值參考,如果一個單詞正好在 wordDict 中,dp[0] 設定成 true 是合理的 dp[0] = true; for (int right = 1; right <= len; right++) { // 如果單詞集合中的單詞長度都不長,從後向前遍歷是更快的 for (int left = right - 1; left >= 0; left--) { // substring 不擷取 s[right],dp[left] 的結果不包含 s[left] if (wordSet.contains(s.substring(left, right)) && dp[left]) { dp[right] = true; // 這個 break 很重要,一旦得到 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(); } } } }