LeetCode Week13: Word Break系列

来源:互联网 发布:手机cf国外视频软件 编辑:程序博客网 时间:2024/06/05 08:39

这周完成的还是Dynamic Programming 部分的题目,这里选择word break系列的两道题目进行分析。

一、Word Break

题目

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.

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

Return true because “leetcode” can be segmented as “leet code”.

我的分析

这道题可以使用动态规划来求解,简单来说,假设dp[i]表示前i个元素组成的序列是否可以正确划分,那么对于一个序列abcdab,长度为6,那么当我们判断abcda都是合法可分符合字典内容时,对于第6个元素:

  • 从第0到第5个元素,从右到左进行一个遍历,即最开始遍历ab是否存在于字典中且a以前的序列是否合法可分,如果满足条件,那么就说明abcdab是合法可分的,如果a不能满足这个条件,那么继续判断dab是否存在于字典中,且前面的序列abc是否可分,依次进行下去;
  • 这里有一个巧妙的点:我们判断ab是否存在的时候,其实前面的序列abcd已经遍历过ab了,那么ab一定会存在于字典中
  • 整个过程的状态转移方程可以写作 dp[i] = I(s[j, i]) & dp[j], j = 0, 1, 2, ……, n。其中I()表示如果s[j,i]存在于字典中,则返回1,否则返回0;

代码

结合分析的结果可得如下代码:

class Solution {public:    bool wordBreak(string s, vector<string>& wordDict) {        int len = s.size();        vector<bool> dp(len+1,false);        dp[0] = true;        for(int i = 1; i <= len; i++)            //从前一个元素开始,判断新加入的元素是否存在            for(int j = i-1; j >= 0; j--){                // 判断新加入的元素是否存在                string str = s.substr(j,i-j);                // 新加入的元素和之前的序列都存在时,说明整个序列都存在                if(dp[j] && find(wordDict.begin(),wordDict.end(),str) != wordDict.end()){                    dp[i] = true;                    break;                }            }        return dp[len];    }};

二、Word Break II

题目

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. You may assume the dictionary does not contain duplicate words.

Return all such possible sentences.

For example, given
s = “catsanddog”,
dict = [“cat”, “cats”, “and”, “sand”, “dog”].

A solution is [“cats and dog”, “cat sand dog”].

我的分析

这道题是上一道题的引申,可以在上一道题的基础上,结合DFS来求解。

  1. 首先,需要判断序列是否可分,在判断序列是否可分中,使用dp[i][j]来表示,以元素i结尾的序列中,可以在j位置划分的合法性,如果在j位置可以划分,那么dp[i][j]为true。例如对于catsand,有两种划分(cat/sand以及cats/and),那么第7行(d是第7个元素),第3列和第4列的值都是true,其他列元素都是false
  2. 在得到不同长度的序列的合法性及划分位置后,可以根据这些信息划分数据加入空格:从序列的原始长度出发,先从后往前得到第一个可划分的单词,之后根据剪掉这个单词之后的剩余序列的划分位置继续做划分,简单来说,在找到一个合法单词后,会把划分这个单词之后的剩余划分情况遍历完毕,之后再往前遍历,这就是一个DFS的过程。

代码

结合上述分析可以得到如下代码:

class Solution {public:    vector<string> wordBreak(string s, vector<string>& wordDict) {        vector<string> res;        if(s.length() == 0 || wordDict.empty()) return res;        // 判断以前i个元素中可以在那些位置划分得到符合条件的一个或多个单词        vector<vector<bool> >dp(s.length()+1, vector<bool>(s.length()));        dp_wordBreak_judge(s, wordDict, dp);        // 得到合法的序列的组合形式        vector<string> tmp;        dp_wordBreak(s, wordDict, res, tmp, dp, s.length());        return res;    }    void dp_wordBreak_judge(string s, vector<string> &wordDict, vector<vector<bool> >&dp){        vector<bool>tmp(s.size()+1,false);        tmp[0] = true;        for(int i = 1; i <= s.size(); i++){            //从前一个元素开始,判断新加入的元素是否存在            for(int j = i-1; j >= 0; j--){                // 判断新加入的元素是否存在                string str = s.substr(j,i-j);                // 新加入的元素和之前的序列都存在时,说明整个序列都存在                if(tmp[j] && find(wordDict.begin(),wordDict.end(),str) != wordDict.end()){                    tmp[i] = true;                    dp[i][j] = true;                }               }        }    }    void dp_wordBreak(string s, vector<string> &wordDict,         vector<string>&res,vector<string> tmp, vector<vector<bool> >&dp, int cur){        int len = s.length();        if(cur == 0){            string str=tmp[tmp.size()-1];            for(int idx = tmp.size()-2; idx >=0; idx--)                str += " "+tmp[idx];            res.push_back(str);            return ;        }        for(int i = 0; i < len; i++){            // 倒序遍历第一个单词            if(dp[cur][i] == true){                // 得到最后一个单词,继续析取前面的序列                tmp.push_back(s.substr(i,cur-i));                dp_wordBreak(s, wordDict, res, tmp, dp,i);                tmp.pop_back();            }        }    }};