Word Break,递归,暂存中间结果,“土地换和平”,dp

本文详细解析了LeetCode上的139题和140题,探讨了如何判断一个字符串能否由字典中的单词组成,并提供了两种解决方案:一种基于递归搜索优化的方法,另一种则是经典的动态规划实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目描述:

给出一个字典,求指定字符串是否能用字典中单词组合而来。Leetcode上分139140两题。

139题只要返回是否能用字典中单词组成该字符串,140题还需要返回所有的可行的分割结果。

二、通用解法

两题是有联系的,可以使用搜索来解决,但是需要使用map结构来暂存中间结果,避免重复搜索相同的子问题。

大多数搜索,都是找到一个策略将"大问题"化为"小问题"。

先说140题,然后将代码稍作修改,就是139题目的方案。


三、代码方案

140题:

运用helper递归函数来做。

能看出其中的猫腻。

result是当前s的结果。

s分为remword

 m这个unordered_map结构是把搜索结果存储的,省去递归对重复子问题的求解。

 去掉unordered_map的相关代码,程序也能完成,但是超时。

class Solution {
public:
	vector<string> wordBreak(string s, const vector<string> &dict_) {
		dict.insert(dict_.begin(), dict_.end());
		return helper(s);
	}//wordBreak
private:
	vector<string> helper(string s) {
		if (m.find(s) != m.end()) return m[s]; //如果有中间结果,就直接取
		vector<string> result;
		if (dict.find(s) != dict.end())result.push_back(s);//对应rem=“”的情况,也是下面for循环中i=0的情况
		int len = s.length();
		for (int i(1); i < len; ++i) {
			string word = s.substr(i);
			if (dict.find(word) == dict.end())continue;
			string rem = s.substr(0, i);
			vector<string> prev = helper(rem);
			int sizeOfPrev = (int)prev.size();
			for (int j(0); j < sizeOfPrev; ++j) {
				prev[j] += " " + word;
			}//for j
			result.insert(result.end(), prev.begin(), prev.end());
		}//for i
		m[s] = result; //存储中间结果
		return result;
	}//helper
	unordered_map<string, vector<string>> m;
	unordered_set<string> dict;
};


139题

m改为unordered_map<string,bool>,

helper返回bool,

result定义为bool类型,

if(prev==true)result=true;

给出代码如下,同样的,如果去掉m这个暂存map的优化,也会超时。

class Solution {
public:
	bool wordBreak(string s, const vector<string> &dict_) {
		dict.insert(dict_.begin(), dict_.end());
		return helper(s);
	}//wordBreak
private:
	bool helper(string s) {
		if (m.find(s) != m.end()) return m[s]; 
		bool result(false);
		if (dict.find(s) != dict.end())result = true;
		int len = s.length();
		for (int i(1); i < len; ++i) {
			string word = s.substr(i);
			if (dict.find(word) == dict.end())continue;
			string rem = s.substr(0, i);
			bool prev = helper(rem);
			if (prev == true)result = true;
		}//for i
		m[s] = result; 
		return result;
	}//helper
	unordered_map<string, bool> m;
	unordered_set<string> dict;
};


四、代码方案。


讨论unordered_map这个暂存结构。在搜索中,通常是使用递归来编程,会遇到相同子问题的情况,这种子问题的求解会重复占用时间。

unordered_map这个暂存结构就是暂存之前的努力成果,将它们保存在内存中。用“土地换和平”。

类似于dp算法的思想。

下面给出用dp来解139题的方案。
这种题目还是dp算法管用。
时间复杂度O(len*len),空间O(len),但还是挺有效的,比起搜索。
dp[i]表示[0,i)是否可以用字典组成。

class Solution {
public:
	bool wordBreak(string s, const vector<string> &wordDict) {
		set<string> wordDictSet(wordDict.begin(),wordDict.end());
		int len = s.length();
		vector<bool> dp(len+1, false);
		dp[0] = true;
		for (int i(1); i <= len; ++i) {
			for (int j(0); j < i; ++j) {
				if (dp[j] && wordDictSet.find(s.substr(j, i - j)) != wordDictSet.end()) {
					dp[i] = true;
					break;
				}//if
			}//for j
		}//for i
		//for (auto tmp : dp)if (tmp)printf("1 "); else printf("0 "); putchar(10);
		return dp[len];
	}//wordBreak
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值