zoukankan      html  css  js  c++  java
  • [LeetCode 139] Word Break

    Given a string s and a dictionary of words dict, determine if s can be break into a space-separated sequence of one or more dictionary words.

    Example

    Given s = "lintcode", dict = ["lint", "code"].

    Return true because "lintcode" can be break as "lint code".

     

    Solution 1. Recursion

    Algorithm:

    For given string s of length n, we first check if s.substring(0, 1) is in dict. If it is, solve a smaller subproblem of checking if s.substring(1, n) is breakable in dict. If s.substring(1, n) is breakable, stop checking and return true; Then check if s.substring(0, 2) is in dict. If it is, solve a smaller subproblem of checking if s.substring(2, n) is breakable in dict. If s.substring(2, n) is breakable, stop checking and return true; and so on.......

     

    We lastly check if s.substring(0, n) is in dict. If it is, solve a smaller subproblem of checking if s.substring(n) is breakable in dict.  s.substring(n) is the base case where all characters have been checked.

     

    However this recursion solution is not efficient as it does a lot of redundant work. For example, for a given string of length 5. 

    s.substring(0, 1) in dict or not && wordBreak on s.substring(1, 5);

    s.substring(0, 2) in dict or not && wordBreak on s.substring(2, 5);  

    s.substring(0, 3) in dict or not && wordBreak on s.substring(3, 5);

    s.substring(0, 4) in dict or not && wordBreak on s.substring(4, 5);

    s.substring(0, 5) in dict or not && wordBreak on s.substring(5);

     

    To solve the subproblem of wordBreak on s.substring(2, 5), the following subproblems are solved.

    s.substring(2, 3) in dict or not && wordBreak on s.substring(3, 5);

    s.substring(2, 4) in dict or not && wordBreak on s.substring(4, 5);  

    s.substring(2, 5) in dict or not && wordBreak on s.substring(5);

     

    To solve subproblem of s.substring(2, 5) we have to solve subproblem of s.substring(3, 5);

    Then after s.substring(2, 5) is solved, s.substring(3, 5) is redundantly solved again as highlighted.

     

     1 public class Solution {
     2     public boolean wordBreak(String s, Set<String> dict) {
     3         if(s == null){
     4             return false;
     5         }
     6         if(s.length() == 0 && dict.size() == 0){
     7             return true;
     8         }
     9         return wordBreakRecursion(s, dict, 0);
    10     }
    11     private boolean wordBreakRecursion(String s, Set<String> dict, int startIdx){
    12         if(startIdx >= s.length()){
    13             return true;
    14         }
    15         for(int i = startIdx; i < s.length(); i++){
    16             if(dict.contains(s.substring(startIdx, i + 1))){
    17                 if(wordBreakRecursion(s, dict, i + 1)){
    18                     return true;
    19                 }  
    20             }
    21         }
    22         return false;
    23     }
    24 }

     

     Solution 2. Dynamic Programming

    To avoid the redundancy, we should use dynamic programming. 

     

    For a given string of length n, if it is breakable, then the last word after breaking must ends at the last character of the given string. We can control the length of the last breakable word from 1 to n.

     

    State: dp[i]: if s[0....... i - 1] can be broken into words in dict.

    Function: dp[i] = true if dp[i - lastWordLen] && dict.contains(s.subtring(i - lastWordLen, i)) for any lastWordLen in [1, i]. if we've already set dp[i] to true, then we skip and procced to calculate dp[i + 1].

    dp[i - lastWordLen]: if the remaining part excluding the last broke word is breakable or not.

    dict.contains(s.subtring(i - lastWordLen, i)): if the last break word of length lastWordLen exists in dict.

    Initialization:

    dp[0] = true;

    Answer: dp[s.length()];

     

    One more optimization trick is to calculate the length of the longest word. Then the algorithm can only break word of at most this length.

     

     1 public class Solution {
     2     public boolean wordBreak(String s, Set<String> dict) {
     3         if(s.length() == 0 && dict.size() == 0){
     4             return true;
     5         }
     6         int n = s.length();
     7         int maxWordLen = getMaxWordLen(dict);
     8         boolean[] dp = new boolean[n + 1]; 
     9         dp[0] = true;
    10         for(int i = 1; i <= n; i++){
    11             dp[i] = false;
    12             for(int lastWordLen = 1; lastWordLen <= maxWordLen && lastWordLen <= i; lastWordLen++){
    13                 if(dp[i - lastWordLen] && dict.contains(s.substring(i - lastWordLen, i))){
    14                     dp[i] = true;
    15                     break;
    16                 }
    17             }
    18         }
    19         return dp[n];
    20     }
    21     private int getMaxWordLen(Set<String> dict){
    22         int maxLen = 0;
    23         for(String word : dict){
    24             maxLen = Math.max(maxLen, word.length());
    25         }
    26         return maxLen;
    27     }
    28 }

     

    Solution 3. Dynamic Programming, Start to break word first from the beginning of the give string.

    The following solution does not use a similar optimization trick as in solution 2, Can you think of a similiar optimization approach?

     

     1 public class Solution {
     2     public boolean wordBreak(String s, Set<String> dict) {
     3         if(s.length() == 0 && dict.size() == 0){
     4             return true;
     5         }
     6         int n = s.length();
     7         boolean[] dp = new boolean[n + 1];
     8         dp[0] = true;
     9         for(int i = 1; i <= n; i++){
    10             dp[i] = false;
    11             for(int j = 0;  j < i; j++){
    12                 if(dp[j] && dict.contains(s.substring(j, i))){
    13                     dp[i] = true;
    14                     break;
    15                 }
    16             }
    17         }
    18         return dp[n];
    19     }
    20 }

     

    Solution 4. Dynamic Programming, with O(n^2) space, n is the length of the given string.

    Algorithm: This dp approach is different with the above approaches.  The core idea is to solve smaller length of substring problems first, then solve bigger length of substring problems.

     

    State: T[i][j]:  if s.substring(i, j + 1) can be broke into words in dict.

    Function: T[i][j] = true, if s.substring(i, j + 1) in dict or there is a k in [i, j) that satisfies T[i][k] == true && T[k][j] == true

    Initialization: T[i][j] = false;

    Answer: T[0][n - 1]

     

     1 public class Solution {
     2     public boolean wordBreak(String s, Set<String> dict) {
     3         if(s.length() == 0 && dict.size() == 0){
     4             return true;
     5         }
     6         int n = s.length();
     7         boolean[][] T = new boolean[n][n];
     8         for(int len = 1; len <= n; len++){
     9             for(int i = 0; i <= n - len; i++){
    10                 if(dict.contains(s.substring(i, i + len))){
    11                     T[i][i + len - 1] = true;    
    12                 }
    13                 else{
    14                     for(int k = i; k < i + len - 1; k++){
    15                         if(T[i][k] && T[k + 1][i + len - 1]){
    16                             T[i][i + len - 1] = true;
    17                             break;
    18                         }
    19                     }
    20                 }
    21             }            
    22         }
    23         return T[0][n - 1];
    24     }
    25 }

     

     

    Related Problems

    Word Break II

  • 相关阅读:
    [BZOJ-1007&洛谷P3194][HNOI2008]水平可见直线--【半平面交(单调栈)】
    [BZOJ-1006&洛谷P3196][HNOI2008]神奇的国度--【图的染色-最大势MCS算法】
    [BZOJ-1005&洛谷P2624][HNOI2008]明明的烦恼-【Purfer序列】py+java
    [BZOJ1211 & 洛谷P2290] [HNOI2004]树的计数-【Purfer序列】py+Java
    【BZOJ-1004&洛谷P1446】[HNOI2008]Cards-置换群(burnside引理|DP)
    POJ-2409 Let it Bead 【置换群-Polya定理】
    Dijkstra--POJ 2502 Subway(求出所有路径再求最短路径)
    二分--LIGHTOJ 1088查找区间(水题)
    二分--1043
    二分---LIGHTOJ 1062
  • 原文地址:https://www.cnblogs.com/lz87/p/7268062.html
Copyright © 2011-2022 走看看