【问题标题】:Hard to understand Leetcode 140 solution难以理解的 Leetcode 140 解决方案
【发布时间】:2020-07-30 20:34:27
【问题描述】:

对于来自 LeetCode https://leetcode.com/problems/word-break-ii/ 的这个问题,我已经看到了下一个解决方案:

class Solution {
private:
    unordered_map<string, vector<string>> dp;
public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        if(dp.find(s) != dp.end())
            return dp[s];
        vector<string> result;
        for(string w : wordDict)
        {
            if(s.substr(0, w.length()) == w)
            {
                if(w.length() == s.length())
                    result.push_back(w);
                else
                {
                    vector<string> temp = wordBreak(s.substr(w.length()), wordDict);
                    for(string t : temp)
                        result.push_back(w + " " + t);
                }
            }
        }
        dp[s] = result;
        return result;
    }
};

有人可以帮我理解它是如何工作的吗?我发现这种递归很难理解。

【问题讨论】:

  • 一般来说比赛代码不是为了被理解而写的。它旨在快速编写并在特定硬件上快速运行一次。很多时候它不值得学习。
  • 理解代码的最好方法是在调试器中启动它并逐行浏览代码。
  • 你试过用调试器看看它是如何工作的吗?

标签: c++ algorithm recursion dynamic-programming


【解决方案1】:

这应该更容易理解:

#include <bits/stdc++.h>
using namespace std;

string s = "pineapplepenapple";
int n;
unordered_set<string> dict({"apple", "pen", "applepen", "pine", "pineapple"});

void solve(vector<string> &v, int index = 0){
    if(index >= n){
        for(int i = 0; i < v.size(); i++){
            cout<<v[i]<<" ";
        }
        cout<<endl;
        return;
    }
    for(int i = index; i < n; i++){
        string sub = s.substr(index, i - index + 1);
        if(dict.find(sub) != dict.end()){
            v.push_back(sub);
            solve(v, i + 1);
            v.pop_back();
        }
    }
}

int main(){
    vector<string> v;
    n = s.size();
    solve(v);
    return 0;
}

输出

pine apple pen apple 
pine applepen apple 
pineapple pen apple 

为了更好地解释,我将solve 分成几部分。

void solve(vector<string> &v, int index = 0){

v 存储每个有效单词以便最后打印。 index 是我们目前正在查看的字符。

    if(index >= n){
        for(int i = 0; i < v.size(); i++){
            cout<<v[i]<<" ";
        }
        cout<<endl;
        return;
    }

这是递归的基本情况,当索引大于或等于字符串本身的大小时,意味着它到达了它的末尾。

    for(int i = index; i < n; i++){
        string sub = s.substr(index, i - index + 1);
        if(dict.find(sub) != dict.end()){
            v.push_back(sub);
            solve(v, i + 1);
            v.pop_back();
        }
    }
}

for 循环查看原始字符串的每个子字符串(从索引开始),例如:ppipinpine、...,当其中之一子字符串在字典中,将其放入v 并再次调用该方法,从该子字符串结束后的索引开始。

最后,当递归返回时,我们从v 中删除子字符串,因为我们想尝试其他的。

提供的解决方案与此解决方案的区别在于,提供的解决方案使用动态规划来存储每个子字符串有多少可能性,因此如果之前已经计算过solve(a, b),则不需要再次计算。这对您来说应该不难扩展。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 2020-04-12
    • 2015-06-19
    • 2018-12-30
    • 1970-01-01
    • 2022-11-28
    相关资源
    最近更新 更多