致虚极 守静笃
刷题笔记0x09:单词拆分
2020-06-25发布 208

题目分析

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。https://leetcode-cn.com/problems/word-break/

这个题目一开始被我误解成是判断列表中的字符串是否都是s的子串,最后看到官方示例才纠正回来~

示例截图

可以发觉,这个题目我们是要找出断点打断字符串s,让打断后的各个单词都在单词列表中。

下面分析一下,如果我们有一个长度为i的字符串,那么假设dp[j]表示字符串到j位置的子字符串是否符合题目条件,可以得出,如果dp[i]也就是长度为i的原字符串s符合条件,那么一定有一个0 < j < i的情况下,dp[j]符合条件,并且,剩下的ji的部分是在单词列表里面的。

通过分析我们发现这是一个可以用动态规划解决的问题,状态转移方程为:

// 伪代码
dp[i] = dp[j] and (s[j:i] in wordDict)

代码实现

在具体代码中,我们设dp数组长度为s长度加一dp[0]设为做初始条件,从i=1开始迭代。

impl Solution {
    pub fn word_break(s: String, word_dict: Vec<String>) -> bool {
        let length = s.chars().count();
        let mut dp: Vec<bool> = vec![false; length + 1];
        dp[0] = true;
        for i in 1..=length {
            for j in 0..i {
                let word = s.as_str()[j..i].to_string();
                if dp[j] && word_dict.contains(&word) {
                    dp[i] = true;
                    break;
                }
            }
        }
        dp[length]
    }
}

示意图

最后成功超越100%,不过这和Leetcode上用Rust刷题的少也有关系。

超100%

优化

我们的代码还有优化的空间,不过由于现在耗时四舍五入0ms了,为了展示差别,用Python演示下(这样感觉Python很没有排面啊:

先上个普通Python版:

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        length = len(s)
        dp = [False] * (length + 1)
        dp[0] = True
        for i in range(1, length + 1):
            for j in range(i):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        return dp[-1]

Python版

由于线性表查找时间复杂度为O(N),而哈希表查找时间复杂度为O(1),所有我们利用字典生成式将原本的单词列表转成哈希结构的字典:

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        length = len(s)
        dp = [False] * (length + 1)
        dp[0] = True
        wordDict = {key: value for value, key in enumerate(wordDict)}
        for i in range(1, length + 1):
            for j in range(i):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] = True
                    break
        return dp[-1]

字典版

快了一丢丢~

评论区也有先求出wordDict中最长单词长度maxWordLength,仅遍历当前i位置向前maxWordLength的元素,以减少循环次数,不过我试了几次运行时间没有显著提升。