子序列相关题目总结
1. 一维dp数组
不连续–最长递增子序列
特点:递增+不连续的子序列
思路:每个节点的dp[i]取决于该节点前边所有节点的状态,前边比它小的节点的最大dp[i] 再加 1
(故使用两层循环)
连续–最长连续递增序列
特点:递增+连续的子序列
思路:每个节点的dp[i]只取决于前一个节点的状态,如果当前节点的dp[i] > dp[i - 1],则直接加1;否则重置为1(一层循环即可)
最大子序和
思路:每个节点只有两个状态:加上/不加,如果加上更大就加,否则就自己做头
2. 二维dp数组
连续–最长重复子数组
特点:两个数组 + 连续重复
思路:使用二维dp数组(小技巧:把dp数组多设置一圈–多头一行和头一列),因为是连续重复,所以可以只记录相等情况即可
不连续–最长公共子序列
特点:两个数组 + 不连续重复
思路:跟连续–最长重复子数组的区别是:也必须记录不相等的情况,因为需要延续状态。当不相等时,就看左边和上边哪个大即可
变形:不相交的线,他们本质都是找一个有序的相同子序列
距离问题(两个字符串的关系)
- 判断子序列:
① 使用双指针
② 动态规划
if((s.charAt(i - 1) == t.charAt(j - 1) ) && dp[i - 1][j - 1]){
dp[i][j] = true;
}
else{
dp[i][j] = dp[i][j - 1] && dp[i - 1][j];
} - 两个字符串的删除操作
当字符相等时:最小操作数dp[i][j] = dp[i - 1][j - 1];
当字符不相等时:最小操作数dp[i][j]执行删除操作①删word1[i - 1],最少操作次数为dp[i - 1][j] + 1;②删word2[j - 1],最少操作次数为dp[i][j - 1] + 1;③同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2 - 编辑距离
当字符相等时:最小操作数dp[i][j] = dp[i - 1][j - 1];
当字符不相等时:最小操作数dp[i][j]可以有三种操作:①替换–dp[i][j] = dp[i - 1][j - 1] + 1; ②删除–dp[i][j] =dp[i - 1][j] + 1;③添加–dp[i][j - 1] + 1;
回文子串
- 连续问题:回文子串
这里的特点是dp数组含义、遍历顺序
①dp数组:二维dp数组–从i到j的子串是不是回文子串
如果s.charAt(i) == s.charAt(j),会出现三种情况:i和j是同一个字母;i和j相连;i和j中间有字母:
if(s.charAt(i) == s.charAt(j)){if(j - i > 1 && dp[i + 1][j - 1]){dp[i][j] = true;res++;}else if(j - i <= 1){dp[i][j] = true;res++;}}
②遍历顺序:
for(int i = len - 1; i >= 0; i--){for(int j = i; j < len; j++){ //j从i开始!!
- 不连续问题:最长回文子序列
①dp数组:二维dp数组–从i到j的回文子串的最大长度
if(s.charAt(i) == s.charAt(j)){dp[i][j] = dp[i + 1][j - 1] + 2;
}
else{dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
②遍历顺序
for(int i = len - 1; i >= 0; i--){dp[i][i] = 1; //初始化for(int j = i + 1; j < len; j++){ //从i+1开始