动态规划之单词拆分
leetcode.139
先决条件
首先看到这题,我们必须明确,至少是一个二重循环以上的算法。因为必须要同时遍历s字符串和wordDict字符数组。
这题用动态规划显然是一个比较优的解法。我们先设一个dp数组,dp[i]的值代表着s下标从0到i是否能用wordDict中的单词拼接。
状态转移方程
对于动态规划,状态转移方程是非常重要的。以下是这个算法的核心代码。
1 | dp[i]=(dp[i-wordDict[j].size()]&&res==wordDict[j]); |
我们来慢慢咀嚼。看一下下面这个案例:
1 | wordDict = ["apple", "pen"] |
对于任意一个dp[i]我们都可以由前面的dp值推出。例如,当i等于8时,dp[8]表示s从1到8这段区间(至于为什么不是从0开始我们后面再说)是否能被拼接。显然是可以的。至于怎么推呢。我们看到,dp[5](apple)显然也是能被拼接的,那么dp[8]是否为true取决于i=5和i=8之间的字符串(pen),显然pen是wordDict数组里的值,因此dp[8]=dp[5]=true.
我们根据上述规律可以建立转移方程:对于任意一个dp[i]我们都可以根据dp[i-wordDict[j].size()]和res是否等于wordDict[j]这两个条件来判断。
dp[i-wordDict[j].size()]表示下标i减去wordDict数组里的一个单词的长度所得到的dp的值。
res表示上述两个下标之间所夹的字符串。
如果dp[i-wordDict[j].size()]和res==wordDict[j]同时成立,那么我们就认为dp[i]是true,有任意一个条件不满足我们就认为dp[i]是false.
代码解决
1 | class Solution { |
为什么下标从1开始
由于dp需要递推,我们需要将初始的dp[0]设置为true才能保证后续的递推。
为什么状态转移方程前面需要加一个特判dp[i]==false
我们知道,wordDict中的字符串是无序的,因此遍历也是无序的,如果一开始就遍历到了能拼接的单词,使得dp[i]=true,但当遇到了不符合的单词,false则会覆盖掉之前的true,导致结果错误。因此我们只对值为false的dp数组进行操作。