leetcode 单词拆分

139 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情

单词拆分

给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。

注意: 词典中的同一个单词可能在分段中被重复使用多次。

  • 1 <= s.length <= 20
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 10
  • s 和 wordDict[i] 仅有小写英文字母组成
  • wordDict 中所有字符串都 不同

解法

这道题有一道简单版本的,就是不统计所有可能的句子,而是判断是否可以分割

题目描述为:

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

我们可以先从这一道题入手本问题。

对于这一道题的简化版本的而言:

可以观察得到一个很典型的状态转移

state[i]=state[j] and check(j+1,i)state[i] = state[j] \ and \ check(j+1, i)

意思就是:

设当前位置为x对于前面的任意一个位置,dp[j]来说,若是dp[j]表示的是前j的长度的字符串可以表示出来则现在我们只需要判断string(j+1,i)这一段是否可以被满足若是两者都满足条件,则一定可以保证dp[x]满足条件设当前位置为 x \\ 对于前面的任意一个位置,dp[j]来说,若是 dp[j]表示的是前j的长度的字符串可以表示出来 \\ 则现在我们只需要判断 string(j+1, i)这一段是否可以被满足 若是两者都满足条件,则一定可以保证 dp[x] 满足条件

所以,这道题用动态规划的思想就很容易的解出来了。

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        unordered_set<string> st;
        for (auto v : wordDict) st.insert(v);
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= i && !dp[i]; j ++) {
                string tm = s.substr(j-1, i-j+1);
                dp[i] |= dp[j-1] & st.count(tm);
            }
        }
        return dp[n];
    }
};

现在,回到本题

现在要求算出具体方案,如何进行求解呢?

其实道理是相通的,核心在于计算其分割点。

现在,我们把眼光逆向看。

假设现在所处位置是x,对于y[x+1,n)若是str(x,y1),满足条件的话,则当前的答案可以由dp[y]转移过来即为dp[x]=dp[y]+word  (str(x,y1)满足条件) 假设现在所处位置是 x,对于 y \in [x+1, n) \\ 若是 str(x, y-1),满足条件的话,则 当前的答案可以由 dp[y]转移过来 \\ 即为 dp[x] = dp[y] + word \ \ (str(x, y-1) 满足条件)\\

现在,只需要逆序的做这一件事情就好了,对于这个而已,用记忆化搜索会比较好写一点,所以,我们可以使用记忆化搜索来写,当然,写为dp从前向后推也是没有问题的,只是枚举顺序发生了改变。


class Solution {
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        unordered_set st(wordDict.begin(), wordDict.end());
        vector<vector<string>> dp(n + 1, vector<string>());
        dp[n].push_back("");
        function<void(int)> dfs = [&](int idx) -> void {
            if (dp[idx].size()) return;
            for (int i = idx + 1; i <= n; i ++) {
                auto tm = s.substr(idx, i - idx);
                if (!st.count(tm)) continue;
                dfs(i);
                for (auto v : dp[i]) {
                    dp[idx].push_back(
                        v.size() == 0 ? tm : tm + " " + v
                    );
                }
            }
        };

        dfs(0);
        return dp[0];
    }
};