动态规划总结(几个常见的序列化问题)
1.最长递增子序列问题
https://leetcode.com/problems/longest-increasing-subsequence/
时间复杂度O(n^2)有些情况会超时
public int lengthOfLIS(int[] nums) { if(nums.length==0||nums==null) return 0; int[] dp=new int[nums.length]; int max=0; for(int i=0;i<nums.length;i++) { dp[i]=1; for(int j=0;j<i;j++) { if(nums[i]>nums[j]){ dp[i]=Math.max(dp[j]+1,dp[i]); } } if(dp[i]>max) max=dp[i]; } return max; }
提供一种新的方法时间复杂度O(n*logn)
public int lengthOfLIS(int[] nums) { if(nums.length==0||nums==null) return 0; int[] ends=new int[nums.length]; ends[0]=nums[0];//ends[i]表示长度为i+1的序列最短的结尾 int len=1; for(int i=1;i<nums.length;i++) { if(nums[i]>ends[len-1]) { ends[len]=nums[i]; len++; } else { int pos=BiSearch(ends, len, nums[i]); ends[pos]=nums[i]; } } return len; } int BiSearch(int[] b, int len, int w) { int left = 0, right = len - 1; int mid; while (left <= right) { mid = left + (right-left)/2; if (b[mid] > w) right = mid - 1; else if (b[mid] < w) left = mid + 1; else //找到了该元素,则直接返回 return mid; } return left;//数组b中不存在该元素,则返回该元素应该插入的位置 }
2.最长回文串问题
https://leetcode.com/problems/longest-palindromic-substring/
public String longestPalindrome(String s) { if(s==null||s.length()==0) return s; int[][] dp=new int[s.length()][s.length()];//dp[i][j]表示第i位到第j位之间的串是不是回文串 int left=0,right=0,maxLen=1; for(int i=0;i<s.length();i++){ dp[i][i]=1; for(int j=i-1;j>=0;j--){ if(s.charAt(i)==s.charAt(j)&&(i-j==1||dp[j+1][i-1]!=0)) dp[j][i]=1; else dp[j][i]=0; if(dp[j][i]!=0&&i-j+1>=maxLen) { left=j; right=i; maxLen=i-j+1; } } } return s.substring(left,right+1); }
和上面类似,最长回文子序列
public static int getLPS(String s) { char[] chars = s.toCharArray(); int length = chars.length; // 第一维参数表示起始位置的坐标,第二维参数表示长度,使用 0 表示长度 1 int[][] dp = new int[length][length]; for (int i = 0; i < length; i++) { dp[i][i] = 1; // 单个字符的最长回文子序列长度为1,特殊对待一下 } //区间dp,外围代表序列的长度 for (int i = 1; i < length; i++) { for (int j = 0; j+i<length; j++) { if (chars[j] == chars[i + j]) { dp[j][i + j] = dp[j + 1][i + j - 1] + 2; } else { dp[j][i + j] = Math.max(dp[j][i + j - 1], dp[j + 1][i + j]); } } } return dp[0][length - 1]; }
3.最长的有效括号的长度
Input: ")()())"
Output: 4
https://leetcode.com/problems/longest-valid-parentheses/
public int longestValidParentheses(String s) { if(s==null||s.length()==0) return 0; Stack<Integer> stack=new Stack<>(); int start=0,maxLen=0; for(int i=0;i<s.length();i++) { if(s.charAt(i)=='(') stack.push(i);//保存下标 else if(s.charAt(i)==')') { if(stack.isEmpty()) start=i+1;//直接跳过这一位,始终保持能够匹配的最左边的下标 else { stack.pop();//匹配弹出 maxLen=stack.isEmpty()?Math.max(maxLen, i-start+1):Math.max(maxLen, i-stack.peek());//举个不为空的例子(((())像这种情况你就得执行i-stack.peek() } } } return maxLen; }
“[]{[]{}}”对于这种有多个可能嵌套的,目前采用这种方法,也是求最长的有效长度
public int MaxValid(char[] ch) { int n=ch.length; int[][] dp=new int[ch.length][ch.length]; int max=0; for(int i=1;i<n;i++) { if(ch[i]=='['||ch[i]=='{') continue; for(int j=i-1;j>=0;j--) { if((ch[i]==']'&&ch[j]=='[')||(ch[i]=='}'&&ch[j]=='{')) { if(i-j==1||dp[j+1][i-1]>0) { dp[j][i]=dp[j+1][i-1]+2; } } for(int k=j-2;k>=0;k--) { if(dp[k][j-1]>0) { dp[k][i]=dp[k][j-1]+dp[j][i]; max=Math.max(dp[k][i], max); } } } } return max; }
4.最长连续数组的和
https://leetcode.com/problems/maximum-subarray/
要保存最大的数组前后下标
public int maxSubArray(int[] nums) { int len = nums.length; if(len == 0){ return 0; } int sum = nums[0]; //sum表示前i-1个数组元素的最大连续子数组的和 int sum1 = nums[0];//sum1表示当前以a[i-1]为结束元素的连续子数组的和 int start=0,end=0; for(int i=1;i<len;i++){ if(sum1>0){//sum1>0则a[i]成为sum1中最后一个元素 sum1 = sum1+nums[i]; end=i; }else{//否则,a[i]成为sum1中唯一的元素 sum1= nums[i]; start=i; } if(sum < sum1){ sum = sum1; } } System.out.println(start+"--"+end); return sum; }
5.能否交叉组成
Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" Output: true
public boolean isInterleave(String s1, String s2, String s3) { if(s1.length()+s2.length()!=s3.length()) return false; int len1=s1.length(); int len2=s2.length(); boolean[][] dp=new boolean[s1.length()+1][s2.length()+1];//dp[i][j]表示s1前i个字符和s2前j个字符组成s3的前i+j个字符 dp[0][0]=true; for(int i=1;i<=len1;i++) dp[i][0]=dp[i-1][0]&&s1.charAt(i-1)==s3.charAt(i-1); for(int j=1;j<=len2;j++) dp[0][j]=dp[0][j-1]&&s2.charAt(j-1)==s3.charAt(j-1); for(int i=1;i<=len1;i++) for(int j=1;j<=len2;j++) { dp[i][j] = (dp[i - 1][j] && s1.charAt(i-1) == s3.charAt(i-1+j) )|| (dp[i][j - 1] && s2.charAt(j-1) == s3.charAt(j-1+i)); } return dp[len1][len2]; }
6.父串里面有多少个子串
Input: S ="rabbbit", T ="rabbit" Output: 3
public int numDistinct(String s, String t) { if(t.length()>s.length()) return 0; int[][] dp=new int[s.length()+1][t.length()+1];//dp[i][j]s串的前i位有多少个t串的前j位 for(int i=0;i<s.length()+1;i++) dp[i][0]=1; for(int j=1;j<t.length()+1;j++) dp[0][j]=0; for(int i=1;i<s.length()+1;i++) for(int j=1;j<t.length()+1;j++) { if(s.charAt(i-1)==t.charAt(j-1)) { dp[i][j]=dp[i-1][j-1]+dp[i-1][j]; } else dp[i][j]=dp[i-1][j]; } return dp[s.length()][t.length()]; }
7.一个字符串通过替换添加删除到另外一个字符串的最小次数
public int minDistance(String word1, String word2) { if(word1==null||word1.length()==0) return word2.length(); if(word2==null||word2.length()==0) return word1.length(); int[][] dp=new int[word1.length()+1][word2.length()+1];//表示word1的前i个字符到word2的前j个字符所需要的步骤 for(int i=1;i<=word1.length();i++) dp[i][0]=i; for(int j=1;j<=word2.length();j++) dp[0][j]=j; for(int i=1;i<=word1.length();i++) for(int j=1;j<=word2.length();j++) { if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j]=dp[i-1][j-1]; else { dp[i][j]=Math.min(Math.min(dp[i-1][j], dp[i][j-1]),dp[i-1][j-1])+1; } } return dp[word1.length()][word2.length()]; }
8.最长公共子序列
public static int LCS(String str1,String str2) { int n=str1.length(),m=str2.length(); int[][] dp=new int[n+1][m+1];//dp[i][j]str1的前i位和str2的前j位的最长公共子序列 for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(str1.charAt(i-1)==str2.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]; }
这里的最长公共子序列可以运用到最长重复子序列,就是加一个相同位置不能相同的条件就可以了(在判断的地方加一个i!=j)
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10434631.html

浙公网安备 33010602011771号