动态规划总结(几个常见的序列化问题)

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)

 

posted @ 2019-02-25 23:28  LeeJuly  阅读(322)  评论(0)    收藏  举报