版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxzxzx0119/article/details/83211864
LeetCode139-Word Break & LeetCode140-Word BreakII (dp)
- LeetCode139 - 记忆化递归
- LeetCode139 - dp
- LeetCode140
LeetCode139题目链接
题目
LeetCode139 - 记忆化递归
递归函数大概的思路:
- 字符串在每个位置从左到右进行划分成左边和右边部分;
- 左边部分递归的去求看是否满足,右边看wordDict中是否有这个单词;
- 因为递归的时候有重复的子问题,所以使用map进行记忆化;
- 这里是从顶(例如"leetcode"从最长求到最短)到下,而dp是从短推到长;
- 这里每个位置只需要某个划分满足即可,所以下面递归函数中如果有一个划分满足条件,立即就return 了;
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null)
return true;
if(wordDict == null)
return false;
// memorize
HashMap<String,Boolean>map = new HashMap<>();
return process(new StringBuilder(s),wordDict,map);
}
private boolean process(StringBuilder sb,List<String> dict,HashMap<String,Boolean>map){
if(dict.contains(sb.toString())) //这句话不能省略 因为下面只是判断到 < sb.length,
return true;
if(map.containsKey(sb.toString()))
return map.get(sb.toString());
for(int i = 0; i < sb.length(); i++){ // for(int i = 1; i < sb.length(); i++){ 也可以写成从1开始 因为L == ""可以不用划分
StringBuilder L = new StringBuilder(sb.substring(0,i)); // [0,i)
StringBuilder R = new StringBuilder(sb.substring(i)); // [i,sb.length)
if(dict.contains(R.toString()) && process(L,dict,map)){//先判断右半部分
map.put(sb.toString(),true);
return true;
}
}
map.put(sb.toString(),false);
return false;
}
}
注意到,也可以反过来改成L去判断在不在wordDict中,而右边部分R去递归(这样代码简介很多,而且速度也更快)
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null)
return true;
if(wordDict == null)
return false;
// memorize
HashMap<String,Boolean>map = new HashMap<>();
return process(s,wordDict,map);
}
//相当于s的左边L去判断在不在wordDict中,而右边去递归
private boolean process(String s,List<String> dict,HashMap<String,Boolean>map){
if(s.isEmpty())
return true; //""返回true
if(map.containsKey(s))
return map.get(s);
for(String word : dict){
if(s.startsWith(word)){// 左边L在wordDict中有包含
if(process(s.substring(word.length()),dict,map)){
map.put(s,true);
return true;
}
}
}
map.put(s,false);
return false;
}
}
LeetCode139 - dp
也就是从下到上的求解:
- 注意,一开始dp.put("",true),表示的是相当于"“是返回true的,这个是必须的。因为某个划分L是”",而R 在wordDict中。
- 比如划分到"leetcode"的"leet"的时候,当leet被划分成"“和"leet"左边就是”",右边是"leet"(在wordDict中),所以满足;
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null)
return true;
if(wordDict == null)
return false;
StringBuilder sb = new StringBuilder(s);
HashMap<String,Boolean> dp = new HashMap<>();
dp.put("",true);// must
for(int i = 1; i <= sb.length(); i++){// 从1开始就可以 因为dp.put("",true)
StringBuilder sbI = new StringBuilder(sb.substring(0,i));
for (int j = 0; j < i; j++) {
String L = sbI.substring(0, j);
String R = sbI.substring(j);
if ( dp.get(L) != null && dp.get(L) && wordDict.contains(R)) {
dp.put(sbI.toString(), true);
break;
}
}
}
return dp.get(sb.toString()) == null ? false : dp.get(sb.toString());
}
}
稍微优化的思路:
- 上面的HashMap中的true其实是表示的dp的值,因为key为String,所以不好用数组表示。
- 但是其实我们可以将左边部分的字符串映射为只需要以某个位置结尾的字符串就可以了。
- 也就是说L部分求解,只需要记录那个字符串的结束位置即可,也就是可以只用一个boolean数组求解即可。
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null)
return true;
if(wordDict == null)
return false;
StringBuilder sb = new StringBuilder(s);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true; // 类似 dp.put("",true);
for(int i = 1; i <= sb.length(); i++){//从1开始就可以
String sbI = sb.substring(0,i);
for (int j = 0; j < i; j++) {
if(dp[j] && wordDict.contains(sbI.substring(j))){
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
再次优化,连sbI也可以省略,因为可以直接取[j,i)之间的字符作为sbI即可。
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null)
return true;
if(wordDict == null)
return false;
StringBuilder sb = new StringBuilder(s);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true; // 类似 dp.put("",true);
for(int i = 0; i <= sb.length(); i++){
for (int j = 0; j < i; j++) {
if(dp[j] && wordDict.contains(sb.substring(j,i))){//这里简单的优化
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
LeetCode140
LeetCode140题目链接
题目
解析
和上题不同的是,这个题目要求出所有的组合:
记忆化递归的方式:
- 在递归的每一层,如果R(右边部分) 包含在wordDict中,则左边求出的所有解(放在List中),都和右边部分的字符串组成一个新的解(新的List),并添加到结果中;
- 注意递归的每一层,一开始要判断没有划分的那个,也就是dict.contains(s)这句。
class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
if (s == null || wordDict == null)
return new ArrayList<>();
return process(s,new HashMap<>(),wordDict);
}
public List<String> process(String s,HashMap<String,List<String>>map,List<String>dict){
if(map.containsKey(s))
return map.get(s);
List<String>res = new ArrayList<>();
if(dict.contains(s))
res.add(s);
for(int i = 1; i < s.length(); i++){//注意这里不需要<=因为是对每一个划分
String R = s.substring(i);
if(!dict.contains(R))
continue;
String L = s.substring(0,i);
List<String>LRes = process(L,map,dict); //先求出左边的结果
for(String si : LRes)
res.add(si + " " + R);
}
map.put(s,res);
return res;
}
}
同理,也可以写成下面的样子(左边查看在不在wordDict中,右边递归) ;
class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
if (s == null || wordDict == null)
return new ArrayList<>();
return process(s,new HashMap<>(),wordDict);
}
public List<String> process(String s,HashMap<String,List<String>>map,List<String>dict){
if(map.containsKey(s))
return map.get(s);
List<String>res = new ArrayList<>();
if(dict.contains(s))
res.add(s);
for(String word : dict){
if(s.startsWith(word)){
String R = s.substring(word.length());
List<String>RRes = process(R,map,dict);
for(String si : RRes)
res.add(word + " " + si);
}
}
map.put(s,res);
return res;
}
}
改成dp的方式超内存了,不知道是写错了还是怎么的,以后刷第二遍的时候再看看吧。。。代码也贴上来:
class Solution {
public List<String> wordBreak(String s, List<String> wordDict) {
if (s == null || wordDict == null)
return new ArrayList<>();
List<String>res = new ArrayList<>();
HashMap<String,List<String>> dp = new HashMap<>();
dp.put("",new ArrayList<>());
for(int i = 1; i <= s.length(); i++){
String sI = s.substring(0,i);
res = new ArrayList<>();
if(wordDict.contains(sI))
res.add(sI);
for(int j = 0; j < i; j++){
String R = sI.substring(j);
if(!wordDict.contains(R))
continue;
List<String>LRes = dp.get(sI.substring(0,j));
for(String si : LRes)
res.add(si + " " + R);
}
dp.put(sI,res);
}
return res;
}
/**
public List<String> wordBreak(String s, List<String> wordDict) {
if (s == null || wordDict == null)
return new ArrayList<>();
List<String>res = new ArrayList<>();
List<List<String>> dp = new ArrayList<>();
dp.add(new ArrayList<>());
for(int i = 1; i <= s.length(); i++){
String sI = s.substring(0,i);
res = new ArrayList<>();
if(wordDict.contains(sI))
res.add(sI);
for(int j = 0; j < i; j++){
String R = sI.substring(j);
if(!wordDict.contains(R))
continue;
List<String>LRes = dp.get(j);
for(String si : LRes)
res.add(si + " " + R);
}
dp.add(i,res);
}
return res;
}
**/
}