leetcode每日一道(10)字符串切分为单词的所有可能的结果

题目描述

给定一个字符串s和一组单词dict,在s中添加空格将s变成一个句子,使得句子中的每一个单词都是dict中的单词
返回所有可能的结果
例如:给定的字符串s =“catsanddog”,
dict =[“cat”, “cats”, “and”, “sand”, “dog”].
返回的结果为[“cats and dog”, “cat sand dog”].

思路

这道题首先想到的就是递归的方法,一个长的字符串,查询到一小部分在词典以内的,那么剩下的部分继续递归就行了。
递归的优化当然就是想想能不能自底向上来解决。因此用动态规划的方法,才是本题的最优方案。
动态规划的几个步骤还记得吗?

  • 1.定义数组含义
  • 2.找到递推关系
  • 3.找出初始值

其实这道题不算是特别典型的动态规划。

定义数组dp

考虑定义这样的一个数组:dp[i][j]是存储的一个01值,表示的是对于字符串s,从i作为起始,一个往后j个字符,这样的一个字符串,是否在字典里。那么可以对字符串进行两层遍历,则将字典转化成了这样一个二维数组,这一步是没有问题的。为什么要搞这样的一个数组,当然是为了方便查询了。

如何查询?

有了dp数组,然后对这个s字符串,如何处理呢
最重要的当然就是如何一个不剩地将这些组合找出来,而“catsanddog”这10个字符,可以形成的分割形态,是指数级别的,其实我们不用都把他们列举出来,比如查到“ca”不在字典里,那么后面的“tsanddog”不论能组成多么天花乱坠的词,都不能行,这就去除了许多冗余的计算。因此,我们就要考虑这个遍历的顺序,究竟是怎么样的!
现在可以肯定的是:字符串,以第一个字符开头的所有词(或者以最后一个字符为结尾的所有词)因为它们都是在边上,所以这些词都要遍历一次才行。接下来提供两种遍历的方法:

  • 前向遍历
    “catsanddog”
    即以c,ca,cat,cats… 的顺序来遍历,考虑如下:当遍历到cat的时候,已经形成了一个单词,那么后面该进行遍历的应该是字串“sanddog”了,那么就递归进去遍历,然后这个子串遍历完了之后,跳出来应该是接着上面的cat,继续查询cats,查到cats,递归进入"anddog",出来之后继续catsa,catsan,catsand…。
  • 后向遍历
    以catsanddog,atsanddog,tsanddog,sanddog…的顺序来遍历。道理和前向遍历类似。
    如果上面您没看懂,下面这个遍历顺序您应该很容易看懂了
    在这里插入图片描述

那么有了这个步骤,后面的代码也就好写了。还有一个问题,就是在每次输出合法答案的时候,怎么实现?这里是用全局变量mystring+递归实现,判定合法的条件之后,把答案添加到result中,但是跳出递归的时候,要记得把mystring中的单词弹出来!这个倒是比较巧妙。
废话不多,看代码:

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <queue>
#include <algorithm>
#include <numeric>        //accmulate
#include <functional>   //greater<int>()!!!
using namespace std;
class Solution {
public:
    vector<string> wordBreak(string s, unordered_set<string> &dict) {
        dp = new vector<bool>[s.size()];
        for (int i=0;i<s.size();i++){
            for (int j=i;j<s.size();j++){
                dp[i].push_back(match(s.substr(i,j-i+1), dict));
            }
        }
        output(s.size()-1, s);
        return result;
    }
    bool match(string s, unordered_set<string> &dict){
        if (dict.find(s)!=dict.end()) return true;
        else return false;
    }
    void output(int i, string s){
        if(i==-1){
            string single ;
            for(int j=mystring.size()-1;j>=0;j--){
                single += mystring[j];
                if (j!=0) single += " ";
            }
            result.push_back(single);
        }
        else{
            for(int k=i;k>=0;k--){
                if (dp[k][i-k]){
                    mystring.push_back(s.substr(k,i-k+1)); //存入合法单词
                    output(k-1,s);
                    mystring.pop_back(); //弹出相应的单词
                }
            }
        }
    }
    
    vector<string> result;
    vector<string> mystring;
    vector<bool> *dp;
};

不懂的留言哦,可能我没写清楚,只有我自己看得懂哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章