LeetCode——139. Word Break

来源:互联网 发布:php面试技巧 编辑:程序博客网 时间:2024/06/14 13:07

问题描述:

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.For example, givens = "leetcode",dict = ["leet", "code"].Return true because "leetcode" can be segmented as "leet code".

  这道题大概的意思就是问一个字符串能不能被拆分成dict中的子字符串,可以的话返回true,不可以的话返回false。
  其实一看到这道题,解题的方法就很明显了,用回溯法,不断地去尝试分割字符串s,并且在前一个子字符串满足的条件下,再继续分割剩余的子字符串,直到原字符串s被分割完,且分割得到的所有子字符串都在dict中,就满足条件了,返回true。下面是递归解决的代码:

    public boolean wordBreak(String s, List<String> wordDict) {        //递归的方法,超时        if(s.length() == 0 || s == null)            return false;        HashSet<String> dict = new HashSet(wordDict);        for(int i = 1; i <= s.length(); i++) {            //递归地进行字符串分割            if(searchWord(s, 0, i, tempSet))                return true;        }        return false;    }    public boolean searchWord(String s, int beginIndex, int endIndex, HashSet<String> wordDict) {        String subString = s.substring(beginIndex, endIndex);            if(wordDict.contains(subString)) {                //当分割到最后一个子字符串时,且这个子字符串能在dict中找到,返回true                if(endIndex == s.length())                    return true;                else {                    for(int i = endIndex+1; i <= s.length(); i++) {                        if(searchWord(s, endIndex, i, wordDict))                            return true;                    }                }            }        return false;    }

  然而,很遗憾的是,这个方法超时了,因为这种递归回溯的方法,并没有对中间的结果进行保存,导致了大量的重复计算。
  因此,我们需要将递归转换为非递归,要怎么转呢?递归是从后往前一步一步往回走,中间的状态不好保存,那我们就可以尝试从前往后走的非递归,它可以很好的保存中间的状态。我们需要一个result数组来保存状态,状态是什么呢,就是关于从0到对应索引组成的子字符串是否能够拆分成字典中单词的状态。那么当我们在判断后面的子字符串是否能够拆分时,如果需要判断前面的子字符串是否能够拆分,就不需要再去通过循环去判断,直接通过状态来判断,就可以少去很多的重复的计算。代码如下:

    public boolean wordBreak(String s, List<String> wordDict) {        //非递归的方法        if(s==null || s.length()==0)              return true;          HashSet<String> dict = new HashSet(wordDict);        //保存动态规划每一步的状态,即从0到当前索引组成的子字符串能否被拆分成字典中的单词的状态        boolean[] result = new boolean[s.length()+1];        result[0] = true;        for(int i = 0; i < s.length(); i++) {            //从开头到当前索引截取子字符串            StringBuilder tempString = new StringBuilder(s.substring(0, i+1));            //判断当前子字符串能否被拆分成字典中的单词            for(int k = 0; k <= i; k++) {                if(result[k] && dict.contains(tempString.toString())) {                    result[i+1] = true;                    break;                }                //删除子字符串的第一个字符,继续判断                tempString.deleteCharAt(0);            }        }        return result[s.length()];    }

  通过这种中间保存状态的方法,我们可以免去递归中很多重复的计算,大大地提高了算法的效率,因此超时的情况也不会再出现了。所以这道题的核心是:非递归的动态规划算法
  谢谢大家观看我的博客。如果有不明白的地方,或者是文中有错误的地方,欢迎指出,谢谢!如果大家喜欢我的博客,也可以给我点点赞。你们的点赞就是我的动力!
  

原创粉丝点击