LeetCode——139. Word Break

问题描述:

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, given
s = "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()];
    }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值