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

  • 相关阅读:
    SpringCloud之初入江湖
    消息中间件RabbitMQ
    分布式搜索引擎ElasticSearch
    MongoDB简介
    SpringBoot和SpringCloud版本对应
    终于有人把Elasticsearch原理讲透了!
    nginx不停服,重新加载配置
    小程序自定义头部标题栏并且自适应各种手机屏幕(滚动头部渐隐渐现)
    Navicat链接数据库报错1130解决方案
    传统的小程序登录 和 云开发小程序登录
  • 原文地址:https://www.cnblogs.com/lz87/p/7268062.html
Copyright © 2011-2022 走看看