[LeetCode] 140. 单词拆分 II(DFS+记忆数组)

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

说明:

  • 分隔时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。

解题思路:
——————————
本题打算详细记录一下解题的思考过程,后面写博客也力求这样写,一开始可能写的语言比较繁琐,后续力求精简,因为有时候会发现,解题思路比解题思路更能启发人,每一个简洁的解题思路背后都是对问题深度思考后的抽象,而这种抽象能力和思考问题的方式、方向、路线等能力更应该是我们需要关注的,有时候虽然傻傻的把一个简单的问题阐述的很深刻,看起来是一件很傻的事,但是任何思考过的事情,哪怕很简单,它也是有价值的。那么本题,我准备详细的记录我对此题一个小时的思考过程,由错误的思路到,正确的思路但是还差一口气,到最终灵活变通的正确代码,以及对别人代码的简单点评。
——————————
本题一开始想到的是回溯算法,此题解题特征很像回溯,字符串在伸缩的过程中找符合要求的字符串,但是回溯算法我们在写代码时,常规的会把结果集作为形参,因为回溯算法本身是深度优先遍历,而DFS算法本身是近似于暴力解法,最后OJ也很听话的报了时间超限。然后想着,遍历的过程中应该会存在很多相同的字符串反复的遍历,以实例1为例,当我们提取了“cats and”和“cat sand”后都要对后面的“dog”做相同的遍历,如果我们能在第一次遍历“dog”的时候把这个字符串对应的遍历结果保存下来,那么在第二次以及之后的遍历中,就可以直接返回结果了,而这种做法在算法解题中有一种叫法,即记忆数组,而DFS+记忆数组也是常见降低DFS时间复杂度的配搭。OK,如果我们需要使用记忆数组,那么常见解题代码中,我们会把结果集在返回的时候赋值给结果数组,那么到这里就能引出脑海中对递归函数形参和返回值正确设计形式的考量了,然后在这儿,我就卡壳了,不知道如何把后面一段字符串截取出来保存在记忆数组中,最后参考了grandyang解法,但是相应的对代码做了微调,即,取字符串s全i个字符前缀(简称字符串A),检查它是否在wordDict中,若在,则获取后n-i个字符后缀能组成的字符串集合,然后逐次与A拼接,那么记忆数组就能映射成m[s]=res了。grandyang在此处的解法更直接,直接扫描wordDict数组,然后判断它的每个元素是否能成为s的前缀,是则做上述判断。

// DFS+memory arr(递归+记忆数组)
// 先判断字符串s前n个字符组成的字符串即,s[:n-1]是否在wordDict中,若在,那么我们
// 只需要先递归得出字符串s[n:]等组成多少种不同字符串,然后依次将s[:n-1]与将这个
// 字符串vector的元素进行拼接,得到回溯时的结果集,Time&Space: O(N)
class Solution {
public:
    vector<string> helper(string s, unordered_set<string>& st) {
    	if (m.count(s)) return m[s];
		if (s.empty()) return {""};
      	vector<string> res;
      	for (int i = 1; i <= s.size(); ++i) {
      		string tmp = s.substr(0, i);	
      		if (!st.count(tmp)) continue;
      		vector<string> rem = helper(s.substr(i), st);
      		for (auto r : rem) {
      			res.push_back(tmp + (r.empty() ? "" : (" " + r)));
			}
		}
		return m[s] = res;
    }
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> st(wordDict.begin(), wordDict.end());
        return helper(s, st);
    }    
private:
	unordered_map<string, vector<string>> m;
};

参考资料

https://www.cnblogs.com/grandyang/p/4576240.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值