zoukankan      html  css  js  c++  java
  • 【LeetCode-动态规划】单词拆分

    题目描述

    给定一个非空字符串 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
    

    题目链接: https://leetcode-cn.com/problems/word-break/

    思路1

    使用动态规划来做。

    • 状态定义:使用 dp[i] 表示从字符串头开始长度为 i 的字符串是否能被拆分为字典中的单词,令边界条件dp[0]=true。例如,对于例子s = "leetcode", wordDict = ["leet", "code"]来说,dp[4]=true,因为 leet 在 wordDict 中。
    • 状态转移公式:dp[i] = dp[j] && (s[j,...,i] in wordDict),j 在 [0, i-1] 之间。还拿上一个例子来说,我们要判断 dp[4] 的值,我们可以看:
      • dp[3] && ("t" in wordDict);
      • dp[2] && ("et" in wordDict);
      • dp[1] && ("eet" in wordDict);
      • dp[0] && ("leet" in wordDict);

    只要这 4 种情况有一种为 true,则说明 "leet" 能被拆分,所以 dp[4]=true。
    代码如下:

    class Solution {
    public:
        bool wordBreak(string s, vector<string>& wordDict) {
            unordered_set<string> lookup(wordDict.begin(), wordDict.end());
            vector<bool> dp(s.size()+1, false);
            dp[0] = true;
            for(int i=1; i<=s.size(); i++){
                for(int j=i-1; j>=0; j--){
                    dp[i] = dp[j] && (lookup.count(s.substr(j, i-j))!=0);
                    if(dp[i]) break;
                }
            }
            return dp[s.size()];
        }
    };
    
    • 时间复杂度:O(n^2)
    • 空间复杂度:O(n)

    思路2

    使用回溯来做。代码如下:

    class Solution {
        bool ans = false;
    public:
        bool wordBreak(string s, vector<string>& wordDict) {
            int start = 0;
            unordered_set<string> lookup(wordDict.begin(), wordDict.end());
            dfs(s, start, lookup);
            return ans;
        }
    
        void dfs(string s, int start, unordered_set<string> lookup){
            if(start>=s.size()){
                ans = true;
                return;
            }
            if(ans) return;
    
            for(int i=start+1; i<=s.size(); i++){ // 注意是 i<=s.size(),不是 i<s.size()
                if(lookup.count(s.substr(start, i-start))>0){
                    dfs(s, i, lookup);
                }
            }
        }
    };
    // 超时
    

    该方法超时未通过。

    思路3

    备忘录算法。我们使用一个备忘录unordered_map<string, bool> memo对思路 2 进行改进,也就是记录下那些不成功的子串分割,如果遇到之前出现的子串并有不成功的记录就不用往下递归了。代码如下:

    class Solution {
        bool ans = false;
    public:
        bool wordBreak(string s, vector<string>& wordDict) {
            int start = 0;
            unordered_set<string> lookup(wordDict.begin(), wordDict.end());
            unordered_map<string, bool> memo;  // 备忘录
            dfs(s, start, lookup, memo);
            return ans;
        }
    
        void dfs(string s, int start, unordered_set<string> lookup, unordered_map<string, bool>& memo){
            if(start>=s.size()){
                ans = true;
                return;
            }
            if(ans) return;
    
            for(int i=start+1; i<=s.size(); i++){ // 注意是 i<=s.size(),不是 i<s.size()
                string sub = s.substr(start, i-start);
                if(lookup.count(sub)>0){
                    if(memo.count(sub)==0 || memo[sub]!=false) dfs(s, i, lookup, memo);
                }
                memo[sub] = false;  // 这样分割不行,记录下来不成功的子串
            }
        }
    };
    
  • 相关阅读:
    浅析Dagger2依赖注入实现过程
    浅谈Java的包装类
    多媒体编程基础之RGB和YUV
    多媒体编程基础之色彩空间
    Android Studio实用快捷键汇总
    Testin云测试平台初体验
    一张图看Goodle Clean设计架构
    一张图看Google MVP设计架构
    WampServer中MySQL中文乱码解决
    使用javamail发送包含八位验证码的邮件(完美解决中文乱码问题)
  • 原文地址:https://www.cnblogs.com/flix/p/13193051.html
Copyright © 2011-2022 走看看