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;
};

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

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