人生的第一篇博客,与诸君共享。
自我介绍:
本人生物硕士在读,在努力自学编程知识和技能,想转JAVA后端开发。
目前,已经在leetcode上开始刷算法和数据结构题了。
在此分享一些刷题的题解和学习心得。
leetcode上1143. 最长公共子序列
作为跨界并自学计算机的小白,
在算法中,我个人认为动态规划是一类很难的算法题目。
在做算法题中,为什么动态规划题目会很难呢?
我认为有两点,首先,对于我来说,可能也是对很多人来说,
1)第一个难点就是,面对一个陌生的题目,
如何准确和快速地判断一个题目是否属于动态规划类型的,
是否能够用上动态规划算法和思想来解决题目。
2)其次就是,当我们确定一个题目是属于动态规划类型题目,
并且可以用动态规划来进行求解后,
接下来最难的就是寻找到动态规划中的***状态转移方程***。
1)对于第一点,
我个人也说不准如何快速和准确地去判断一个题目是否是动态规划题,
所以,
为了提高对动态规划类型题目的灵敏度,
只能去多刷题了,在刷题中总结和找感觉。
2)对于第二点的话,我目前的感受是:
动态规划通常是用于求最优解,动态规划在求解最优解的过程中,
除了base case外,每一步都涉及到选择问题,
每一步的选择就带来了状态的转移,
所以,在设计状态转移方程的时候,
首先要弄明白两点:
(1)题目中的有哪些变量的状态会发生改变?
(2)每次进行选择时,有哪些可选项?
这些可选项会导致变量的状态发生什么变化?
明白了上面两点,状态转移方程就好弄了。
动态规划其实和递归思想、回溯算法类似,
动态规划的本质是穷举,然后找到最优解。
但是呢,动态规划往往要进行剪枝优化的,
这样使得动态的规划的效率就区别于普通的递归和回溯算法了。
在1143题的最长公共子序列中,
题目中给了两个字符串st1, st2,
对于给定的这两个字符串变量是没办法也没必要改变的,
但是呢,遍历字符串st1的指针i1,和遍历字符串st2的指针i2,
这两个变量代表了字符串的状态,是随时发生变化的,
所以,变量i1和i2代表的字符串状态会发生改变。
但是,i1和i2的状态如何发生变化呢?
这就跟接下来的选择有关了,
以下的选择均是自顶向下(即从后往前遍历和比较两个字符串):
(1) 如果当前的st1.charAt(i1)和st2.charAt(i2)相等,
这时的选择是: i1和i2均往前移动一位;
(2) 如果当前的st1.charAt(i1)和st2.charAt(i2)不相等,
这时的选择可能是:
(a) 只需要i1往前移动一位而i2状态不变,
(b) 或者i1状态不变而i2往前移动一位;
然后,在上述这三种选择中寻找到最优解(最长的公共子序列).
所以状态转移方程就可以出来了:
(1) st1.charAt(i1)和st2.charAt(i2)相等:
dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1]+1);
(2) st1.charAt(i1)和st2.charAt(i2)不相等,
i1往前移动一位而i2状态不变:
dp[i][j] = Math.max(dp[i][j], dp[i-1][j]);
(3) st1.charAt(i1)和st2.charAt(i2)不相等,
i1状态不变而i2往前移动一位:
dp[i][j] = Math.max(dp[i][j], dp[i][j-1]);
所以,
最终只需要在这三种选择导致的状态中找到最长的公共子序列即可。
(该题解的效率不高,但本菜鸡好歹稍微理解了动态规划。)
以下是个人的JAVA代码
题目描述:
1143. 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:
它是由原字符串在不改变字符的相对顺序的情况下删除某些字符
(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-common-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
int[][] dp;
boolean[][] visited;
public int longestCommonSubsequence(String text1, String text2) {
int len1 = text1.length();
int len2 = text2.length();
dp = new int[len1+1][len2+1];
visited = new boolean[len1][len2];
int output = helper(len1-1, len2-1, text1, text2);
return output;
}
private int helper(int i1, int i2, String st1, String st2){
if(i1<0||i2<0){
return 0;
}
if(visited[i1][i2]==true){
return dp[i1][i2];
}
if(st1.charAt(i1)==st2.charAt(i2)){
dp[i1][i2] = Math.max(helper(i1-1,i2-1,st1, st2)+1, dp[i1][i2]);
}else{
dp[i1][i2] = Math.max(dp[i1][i2], helper(i1,i2-1,st1, st2));
dp[i1][i2] = Math.max(dp[i1][i2], helper(i1-1,i2,st1, st2));
}
visited[i1][i2]=true;
return dp[i1][i2];
}
}