[LeetCode] World Break

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

For example, given
s = "leetcode",
dict = ["leet", "code"].

Return true because "leetcode" can be segmented as "leet code".

这题是经典的dp问题,我之前一直都没搞清楚。今天终于一次写出来accept了。最早使用dfs做的,可想而知,直接超时。

这就像斐波那契数列那样,如果你用递归,大部分时间都在做重复的工作。换成这个题,思路也是一样的,那如何减少工作量?就是把之前算出来的结果cache起来。

dp的逻辑普遍不好想,所以要经常联系如何用dp的角度考虑问题。这种字符类的算法题,一半和dp有关。

思路:将大问题分解成小问题:

比如输入是“abcd” ,字典我们不知道:

我们倒着想,如果abcd是可以break的,根据题意,要么abcd在字典里,要么abcd的两个或者多个部分就在字典里。([a,bcd] or [a,b,cd]之类的情况)

也就是说 abc如果是可以break的,那么如果d在字典里,必然abcd就是break的。如果这一点想通了,下面的推导和分析就是把思路转化为普遍情况的程序的过程。


从最开始遍历到最后,建立一个boolean数组来存结果dp[n+1], n是字符串s的大小。 

我们定义,dp[]的意义在于,dp[i]代表着到第i个substring的时候,是否能分解。

比如说: dp[2] 那就代表 s.substring(0,2)的时候的情况(也就是 "ab"是否可以break)。 dp[0]算是一个base case,因为我们每次都在拿前面的结果来推断现在的情况,那么最开始的时候(“a”)可以使用dp[0]的结果,所以多加一个会方便很多。

为啥要用dp[n+1],这其实也是方便计算。看别人代码的时候,我就在想,莫非都要多申明一个空间?其实不然,你也可以用dp[n],但是就是得加点判断,麻烦点。在熟练dp的思路以后,这些细节都可以自己做处理。

最关键的一点就是搞清dp的递推式:dp[i]=?

这个题不难想:根据上面的分析 如果要dp[i]成立

(1)那么要么s.substring(0,i)是在字典里的,比如说abcd, [a,abcd] 

(2)要么存在一个节点 j,使得s.substring(0,j) 是可以break, 而且s.substring(j,i)在字典里. 比如说 abcd [a,b,cd]

那么ab 是可以break的,cd在字典里, dp[i]=true;

你可能会想到,要是多个字符的时候,那怎么判断。我在想dp问题的时候,总会看到各种各样的分支情况,干扰了思维。

其实不用担心,我们从左到右遍历的时候,已经把这个情况调查完了,结果已经存在了dp中。只要任意的dp[k]==true,那就说明s.substring(0,k)必然是可以break的。所以只要存在一个(2)这样的情况,dp[i]=true; 所以dp的好处是不用做重复的工作. 

所以我觉得dp的解法就是 (1)定义数组含义dp[], dp[][]代表什么?(2)分析子问题  (3)推导递推公式  (4)完成for循环 以及具体操作, 计算出dp[i]

下面的代码综合了(1)和(2)的情况:因为dp[0]是true的,那么我们肯定会看一下substring(0,i)是不是在dict里。所以两层for循环, 第二层是为了找出是否存(2)这样的情况使dp[i]=true;

    public boolean wordBreak(String s, Set<String> dict) {
        int n= s.length();
        boolean[] dp=new boolean[n+1];
        dp[0]=true;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=i;j++){
                if(dp[j] && dict.contains(s.substring(j,i))){
                    dp[i]=true;
                }
            }
        }
        return dp[n];
    }





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值