一、题目
1、单词拆分(LC 139)
2、最长递增子序列(LC 300)
二、题解
1、单词拆分(LC 139)

(1)分析
这道题要求判断一个字符串能否按照字典里的单词进行拆分,本质是一个递归选择问题,可以用记忆化 DFS来做。
整体思路是从字符串的末尾往前拆分,每次从后往前截取一段单词,如果这段单词刚好在字典里,就继续递归判断前面剩下的字符串能不能继续拆分,直到把整个字符串拆完。为了提高查询速度,可以先把字典里的单词放进 HashSet,这样判断单词是否存在的速度会快很多,同时提前找出字典里最长单词的长度,用来限制截取范围,避免做无用的截取操作。
递归函数的核心是记录当前处理到字符串的哪个位置,用 i 表示当前处理到第 i 个字符,当 i 等于 0 时,说明整个字符串已经成功拆分完毕,直接返回成功。为了避免重复递归,使用一个 memo 记忆数组,记录每个位置能否成功拆分,如果已经计算过就直接返回结果,不用再重复执行递归。
在递归内部,从当前位置往前截取单词,长度不超过字典最长单词长度,只要找到一段存在于字典中的单词,并且前面的部分也能成功拆分,就说明当前位置是可以拆分的,记录结果并返回。如果遍历完所有可能都无法拆分,就记录为不可拆分。
(2)解答
java
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n = s.length(); //字符串的长度
int maxLen = 0; //字典字符串的最大长度
for(String str : wordDict){
maxLen = Math.max(maxLen, str.length()); //找出字典字符串的最大长度
}
Set<String> word =new HashSet<>(wordDict); //用HashSet集合,提高查询效率
int[] memo = new int[n + 1]; //记忆数组
Arrays.fill(memo, -1); //初始化元素为-1
return dfs(n, maxLen, word, s, memo) == 1;
}
public int dfs(int i, int maxLen, Set<String> word, String s, int[] memo){
if( i == 0){ //成功拆分
return 1;
}
if(memo[i] != -1){ //已经计算过
return memo[i];
}
for(int j = i - 1; j >= Math.max(0, i - maxLen); j--){
if(word.contains(s.substring(j, i)) && dfs(j, maxLen, word, s, memo) == 1){
return memo[i] = 1; //字典集合中存在当前字符串,且前面的字符串也能够成功拆分
}
}
return memo[i] = 0;
}
}
2、最长递增子序列(LC 300)

(1)分析
这道题求数组里最长的递增子序列长度,也可以用记忆化 DFS来实现。
核心思路是:以数组中每一个元素作为子序列的结尾,分别求出以它结尾的最长递增子序列长度,最后在所有结果里取最大值,就是整个数组的答案。递归函数的作用就是计算以第 i 个元素结尾的最长递增子序列长度,这样问题就被拆分成一个个更小的子问题。
在递归过程中,遍历 i 前面所有的元素,如果某个元素比 nums [i] 小,说明可以把 nums [i] 接在以这个元素结尾的子序列后面,形成更长的递增子序列。在所有符合条件的结果里选出最大的那一个,再加上当前元素本身,就是以 i 结尾的最长长度。为了避免重复计算,用 memo 数组记录已经算过的结果,只要某个位置算过,就直接返回,不用再重新递归。最后所得的 ans 值即为所求。
(2)解答
java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length; //数组长度
int[] memo = new int[n]; //记忆数组,存放以每个元素结尾的最长子序列大小
int ans = 0;
for(int i = 0; i < n; i++){
ans = Math.max(ans, dfs(i, memo, nums)); //dfs(i) 以nums[i]结尾的最长子序列的长度
}
return ans;
}
public int dfs(int i, int[] memo, int[] nums){
if(memo[i] != 0 ){ //说明计算过了
return memo[i];
}
int res = 0;
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]){ //找到j<i且 nums[j]<nums[i],取最大的存放在memo[i]
res = Math.max(res, dfs(j, memo, nums));
}
}
res++; //比前面最大的多一个
return memo[i] = res;
}
}