Day07 公共子串、子数组问题
1、最长重复子数组
- 思路:
- DFS直接进行暴力回溯搜索.超时
注意区别月最长公共子串的DFS。在数组不是公共数组时,要及时停止DFS搜索
class Solution { public int findLength(int[] nums1, int[] nums2) { int res = 0; for (int i = 0; i < nums1.length; i++) { for (int j = 0; j < nums2.length; j++) { res = Math.max(dfs(nums1, nums2, i, j), res); } } return res; } //递归思路不同于最长公共子序列。子数组在不公共时停止深入搜索 private int dfs(int[] num1, int[] num2, int index1, int index2) { if (index1 >= num1.length || index2 >= num2.length) { return 0; } if (num1[index1] != num2[index2]) return 0; else return dfs(num1, num2, index1 + 1, index2 + 1) + 1; } }- DP打表优化
class Solution { int[][] dp; public int findLength(int[] nums1, int[] nums2) { int res = 0; int m = nums1.length; int n = nums2.length; dp = new int[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { res = Math.max(dfs(nums1, nums2, i, j), res); } } return res; } //递归思路不同于最长公共子序列。子数组在不公共时停止深入搜索 private int dfs(int[] num1, int[] num2, int index1, int index2) { if (index1 >= num1.length || index2 >= num2.length) { return 0; } if (num1[index1] != num2[index2]) return 0; else { if (dp[index1][index2] != 0) return dp[index1][index2]; dp[index1][index2] = dfs(num1, num2, index1 + 1, index2 + 1) + 1; } return dp[index1][index2]; } }- 有公共子问题,思考如何使用动态规划的思路,从子问题出发解决问题
class Solution { public int findLength(int[] nums1, int[] nums2) { int ans = 0; int m = nums1.length; int n = nums2.length; //创建DP数组,dp[i][j]表示以i-1、j-1结尾的公共子数组长度。 int[][] dp = new int[m + 1][n + 1]; //初始化,当i、j分别取1时 for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { if (nums1[i - 1] == nums2[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } ans = Math.max(dp[i][j], ans); } } return ans; } }- 空间复杂度优化
- 画图分析,dp[i][j]只与dp[i-1][j-1]有关,直接压缩成dp[j]
class Solution { public int findLength(int[] nums1, int[] nums2) { int ans = 0; int n = nums2.length; //创建DP数组,dp[i][j]表示以i-1、j-1结尾的公共子数组长度。 int[] dp = new int[n + 1]; //初始化,当i、j分别取1时 for (int i = 1; i <= nums1.length; i++) { //滚动数组,下一次循环用到上一次循环构建的数组 for (int j = n; j > 0; j--) { if (nums2[j - 1] == nums1[i - 1]){ dp[j] = dp[j - 1] + 1; }else { //必须进行滚动覆盖!!! dp[j] = 0; } ans = Math.max(dp[j],ans); } } return ans; } }
2、最长公共子序列
- 思路
- 暴力回溯+打表优化
- 注意跟题1的DFS做区分。区别在不匹配是否还向后搜索
- 子序列不用连续,不匹配需要继续向后搜索
class Solution { int[][] dp; public int longestCommonSubsequence(String text1, String text2) { dp = new int[text1.length()][text2.length()]; for (int i = 0; i < text1.length(); i++) { for (int j = 0; j < text2.length(); j++) { dp[i][j] = -1; } } return dfs(text1, text2, 0, 0); } private int dfs(String text1, String text2, int index1, int index2) { if (index1 >= text1.length() || index2 >= text2.length()) { return 0; } if (dp[index1][index2] != -1) return dp[index1][index2]; if (text1.charAt(index1) == text2.charAt(index2)) { dp[index1][index2] = dfs(text1, text2, index1 + 1, index2 + 1) + 1; } else { dp[index1][index2] = Math.max(dfs(text1, text2, index1 + 1, index2 + 1) , Math.max(dfs(text1, text2, index1 + 1, index2) , dfs(text1, text2, index1, index2 + 1))); } return dp[index1][index2]; } }- 有重叠子问题,试试动态规划。
class Solution { public int longestCommonSubsequence(String text1, String text2) { int n = text1.length(); int m = text2.length(); //DP 数组表示前i、j个字符的最长公共子序列 // i、j位置字符相同,则结果为dp[i-1][j-1]+1 //否则 应该是 dp[i-1][j] dp[i][j-1]的最大值 int[][] dp = new int[n + 1][m + 1]; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (text1.charAt(i - 1) == text2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } return dp[n][m]; } }