【LeetCode】单词拆分

332 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第16天,点击查看活动详情

单词拆分

原题:leetcode-cn.com/problems/wo…

描述

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明: 分时可以重复使用字典中的单词 你可以假设字典中没有重复的单词

难度

中等

示例

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

思路

将问题拆解成判断前 i 个字符组成的字符串是否可以被空格拆分为一个或多个在字典中出现的单词。设 s[0..i - 1] 为前 i 个字符组成的字符串,dp[i] 为前 i 个字符组成的字符串是否可以被空格拆分为一个或多个在字典中出现的单词,枚举前 i 个字符组成的字符串,枚举时下标为 j,判断 s[0..j -1] 组成的字符串和 s[j..i-1] 组成的字符串是否都符合题意,如果两者都符合,那么前 i 个字符串构成的字符串也是符合题意的。dp[j] 已经表示 s[0..j -1] 组成的字符串是否符合题意,所以只要判断 s[j..i-1] 组成的字符串是否符合题意即可,于是可以得出状态转移方程:

dp[i] = dp[j] && contains(s[j:i])

contains(s[j:i]) 表示判断 s[j..i-1] 组成的字符串是否出现在字典中,对于判断字符串是否出现在字典中可以使用哈希表来判断。注意 dp[0] 默认为 true。

代码

Rust

pub struct Solution {}
​
impl Solution {
    pub fn word_break(s: String, word_dict: Vec<String>) -> bool {
        let str = s.as_str();
        let mut word_dict_set = std::collections::HashMap::new();
        // 将字典数组放入哈希表
        for str in word_dict.into_iter() {
            word_dict_set.insert(str, true);
        }
        let s_len = str.len();
        // 存储前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
        let mut dp = vec![false; s_len + 1];
        // 表示空串能够拆分
        dp[0] = true;
        for i in 1..=s_len {
            // 判断前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
            for j in 0..i {
                // 判断前 j 个字符组成的字符串和剩余的 s[j, i - 1] 是否在字典中出现
                if let Some(exist) = word_dict_set.get(&str[j..i]) {
                    if dp[j] && *exist {
                        dp[i] = true;
                        // 只要可以被拆分, 停止继续搜索
                        break;
                    }
                }
            }
        }
        dp[s_len]
    }
}
#[test]
fn test_word_break() {
    let s = "leetcode".to_string();
    let word_dict = vec!["leet".to_string(), "code".to_string()];
    println!("s={}, wordDict={:?}", s, word_dict);
​
    let result = Solution::word_break(s, word_dict);
    println!("{}", result);
​
    let s = "applepenapple".to_string();
    let word_dict = vec!["apple".to_string(), "pen".to_string()];
    println!("s={}, wordDict={:?}", s, word_dict);
​
    let result = Solution::word_break(s, word_dict);
    println!("{}", result);
​
    let s = "catsandog".to_string();
    let word_dict = vec![
        "cats".to_string(),
        "dog".to_string(),
        "sand".to_string(),
        "and".to_string(),
        "cat".to_string(),
    ];
    println!("s={}, wordDict={:?}", s, word_dict);
​
    let result = Solution::word_break(s, word_dict);
    println!("{}", result);
}

运行结果:

s=leetcode, wordDict=["leet", "code"]
true
s=applepenapple, wordDict=["apple", "pen"]
true
s=catsandog, wordDict=["cats", "dog", "sand", "and", "cat"]
false

Go

func wordBreak(s string, wordDict []string) bool {
    wordDictSet := make(map[string]bool)
    // 将字典数组放入哈希表
    for _, str := range wordDict {
        wordDictSet[str] = true
    }
    sLen := len(s)
    // 存储前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
    dp := make([]bool, sLen + 1)
    // 表示空串能够拆分
    dp[0] = true
    for i := 1; i <= sLen; i++ {
        // 判断前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
        for j := 0; j < i; j++ {
            // 判断前 j 个字符组成的字符串和剩余的 s[j, i - 1] 是否在字典中出现
            if dp[j] && wordDictSet[s[j:i]] {
                dp[i] = true
                // 只要可以被拆分, 停止继续搜索
                break
            }
        }
    }
    return dp[sLen]
}
func TestWordBreak(t *testing.T) {
    s := "leetcode"
    wordDict := []string{"leet", "code"}
    t.Logf("s=%s, wordDict=%v\n", s, wordDict)
​
    result := wordBreak(s, wordDict)
    t.Log(result)
​
    s = "applepenapple"
    wordDict = []string{"apple", "pen"}
    t.Logf("s=%s, wordDict=%v\n", s, wordDict)
​
    result = wordBreak(s, wordDict)
    t.Log(result)
​
    s = "catsandog"
    wordDict = []string{"cats", "dog", "sand", "and", "cat"}
    t.Logf("s=%s, wordDict=%v\n", s, wordDict)
​
    result = wordBreak(s, wordDict)
    t.Log(result)
}

运行结果:

s=leetcode, wordDict=[leet code]
true
s=applepenapple, wordDict=[apple pen]
true
s=catsandog, wordDict=[cats dog sand and cat]
false

Java

public class Main {
​
    public static boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordDictSet = new HashSet<>(wordDict);
        int sLen = s.length();
        // 存储前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
        boolean[] dp = new boolean[sLen + 1];
        // 表示空串能够拆分
        dp[0] = true;
        for (int i = 1;i <= sLen; i++) {
            // 判断前 i 个字符组成的字符串是否能被空格拆分成若干个字典中出现的单词
            for (int j = 0; j < i; j++) {
                // 判断前 j 个字符组成的字符串和剩余的 s[j, i - 1] 是否在字典中出现
                if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    // 只要可以被拆分, 停止继续搜索
                    break;
                }
            }
        }
        return dp[sLen];
    }
​
    public static void main(String[] args) {
        String s = "leetcode";
        List<String> wordDict = Arrays.asList("leet", "code");
        StringBuilder sb = new StringBuilder("[");
        for (String word : wordDict) {
            sb.append(word).append(",").append(" ");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");
        System.out.printf("s=%s, wordDict=%s\n", s, sb);
​
        boolean result = wordBreak(s, wordDict);
        System.out.println(result);
​
        s = "applepenapple";
        wordDict = Arrays.asList("apple", "pen");
        sb = new StringBuilder("[");
        for (String word : wordDict) {
            sb.append(word).append(",").append(" ");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");
        System.out.printf("s=%s, wordDict=%s\n", s, sb);
​
        result = wordBreak(s, wordDict);
        System.out.println(result);
​
        s = "catsandog";
        wordDict = Arrays.asList("cats", "dog", "sand", "and", "cat");
        sb = new StringBuilder("[");
        for (String word : wordDict) {
            sb.append(word).append(",").append(" ");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");
        System.out.printf("s=%s, wordDict=%s\n", s, sb);
​
        result = wordBreak(s, wordDict);
        System.out.println(result);
    }
}

运行结果:

s=leetcode, wordDict=[leet, code]
true
s=applepenapple, wordDict=[apple, pen]
true
s=catsandog, wordDict=[cats, dog, sand, and, cat]
false