递归函数大概的思路:
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 rec(s, wordDict, map);
}
private boolean rec(String s, List<String> dict, HashMap<String, Boolean> map) {
if (dict.contains(s)) //这句话不能省略 因为下面只是判断到 < sb.length,
return true;
if (map.containsKey(s))
return map.get(s);
for (int i = 0; i < s.length(); i++) { // for(int i = 1; i < sb.length(); i++){ 也可以写成从1开始 因为L == ""可以不用划分
String L = s.substring(0, i); // [0,i)
String R = s.substring(i); // [i,sb.length)
if (dict.contains(R) && rec(L, dict, map)) {//先判断右半部分
map.put(s, true);
return true;
}
}
map.put(s, false);
return false;
}
}
注意到,也可以反过来改成L去判断在不在wordDic
t中,而右边部分R
去递归(这样代码简介很多,而且速度也更快)
class Solution {
private HashMap<String, Boolean> dp;
public boolean wordBreak(String s, List<String> wordDict) {
if (s == null)
return true;
if (wordDict == null)
return false;
dp = new HashMap<>();
return rec(s, wordDict);
}
//相当于s的左边L去判断在不在wordDict中,而右边去递归
private boolean rec(String s, List<String> dict) {
if (s.isEmpty())
return true; //""返回true
if (dp.containsKey(s))
return dp.get(s);
for (String word : dict) {
if (s.startsWith(word)) {// 左边L在wordDict中有包含
if (rec(s.substring(word.length()), dict)) {
dp.put(s, true);
return true;
}
}
}
dp.put(s, false);
return false;
}
}
递推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;
HashMap<String, Boolean> dp = new HashMap<>();
dp.put("", true);// must
for (int i = 1; i <= s.length(); i++) {// 从1开始就可以 因为dp.put("",true)
String sI = s.substring(0, i);
for (int j = 0; j < i; j++) {
String L = sI.substring(0, j);
String R = sI.substring(j);
if (dp.get(L) != null && dp.get(L) && wordDict.contains(R)) {
dp.put(sI.toString(), true);
break;
}
}
}
return dp.get(s) == null ? false : dp.get(s);
}
}
稍微优化的思路:
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()];
}
}
和上题不同的是,这个题目要求出所有的组合:
记忆化递归的方式:
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 wordBreak(String s, List wordDict) {
if (s == null || wordDict == null)
return new ArrayList<>();
Listres = new ArrayList<>();
List> 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;
ListLRes = dp.get(j);
for(String si : LRes)
res.add(si + " " + R);
}
dp.add(i,res);
}
return res;
}
**/
}