leetcode动态规划(持续更新)

5. 最长回文子串

思路分析:

先看一个成功的例子:abba,A[1]和A[4]匹配,A[2]和A[3]匹配,更符合实际来看,当我们不看A[4]时,abb,最长回文串为bb,当我们更新到abba时候,最长回文串更新到abba,所以整个的循环不变量是相应的最长回文串

ji
abba

dp:

初始化:

0000
0000
0000
0000

结果:

i\j0123
01
101
2011
31001

递推方程:

f(i)(j):j到i是否形成了回文串

递推方程:
f ( i ) ( j ) = { 1 , i − j < = 1 d p [ i − 1 ] [ j + 1 ] , i − j > 1 f(i)(j)=\begin{cases} 1,i-j<=1\\ dp[i-1][j+1], i-j>1\end{cases} f(i)(j)={1ij<=1dp[i1][j+1]ij>1
代码实现:

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        if(n<1){
            return "";
        }
        int dp[][] = new int[n][n];
        int start = 0;
        int end = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                if (s.charAt(i)==s.charAt(j)){//i,j位置是一样
                    if (i-j<=1 || dp[i-1][j+1]==1){
                        dp[i][j] = 1;
                        if (i-j>=end-start){
                            end = i;
                            start = j;
                        }
                    }
                }
            }
        }
        return s.substring(start,end+1);
    }
}

对存储空间做出优化(下三角可以优化为一维数组):

dp[i] [j] -->dp[i(i+1)/2+j]

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        if(n<=0){
            return "";
        }
        int length = n*(n+1)/2;
        int dp[] = new int[length];
        int start = 0;
        int end = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                if (s.charAt(i)==s.charAt(j)){
                    if (i-j<=2 || dp[ (i-1)*i/2+j+1]==1){
                        dp[ i*(i+1)/2+j] = 1;
                        if (i-j>=end-start){
                            end = i;
                            start = j;
                        }
                    }
                }
            }
        }
        return s.substring(start,end+1);
    }
}

32. 最长有效括号

示例:()(())

输出:6

示例:(()()

输出: 4

分析:以()()()为例,首先(单个是不能形成有效括号的,当我们遍历这个串的时候,()能形成配对,同理,()()也能形成配对。扩展一下,能形成配对的一定是 ) 结尾的,例如:(())

当子串以(结尾时,无论如何都是匹配到另一半的,因为)一定在其左边,都已经是结尾了,哪来的左边

确定了以)结尾之后,我们就考虑其前一个位置元素是什么,

  1. 前一个为 ( 直接配对

  2. 前一个为 ) ,我们需要找到 ) 的最大的形成的串是多长,然后在寻找其和结尾配对的位置是否是(

    ​ 例如:

    01234 i

    当i指向最后一个位置的时候,我们发现其前一个,也就是三号位是 ) 我们找到这个)匹配的最大配对括号,为2,然后判断4 - 2 - 1,也就是1的位置是否是(,如果是,则4位置的最大配对子串就是4了,当然了这还是有一个小问题的,我们再看另一个例子:

    0123456

    当i指向5的时候,通过上一例子,不难判断,子串为4,但是,实际为6,因为01位置形成的串是可以和2345结合起来的,所以我们得出了以下的递推方程

    dp[i]:表示[0,i]以s.charAt(i)结尾的形成的最大的配对子串
    d p [ i ] = { d p [ i − 2 ] + 2 , 前 一 个 位 置 为 ( d p [ i − d p [ i − 1 ] − 2 ] + d p [ i − 1 ] + 2 , 前 一 个 位 置 为 ) dp[i] = \begin{cases} dp[i-2] + 2 ,前一个位置为( \\dp[i-dp[i-1]-2]+dp[i-1]+2 ,前一个位置为)\end{cases} dp[i]={dp[i2]+2,(dp[idp[i1]2]+dp[i1]+2,)

class Solution {
   public static int longestValidParentheses(String s) {
		//最后一位是)才行,
		//1,前一位是(匹配
		//2,前一位是),找到和)的匹配的(,其前一位是(,匹配成功,反则之
		int max = 0;
		int n = s.length();
		int dp[] = new int[n];
		for(int i = 1;i < n;i++) {
			if((s.charAt(i)==')')) {
				if((s.charAt(i-1) == ')')) {
					if((i-1-dp[i-1]>=0) && (s.charAt(i-1-dp[i-1]) == '(')) {
						dp[i] = i-dp[i-1]-2>=0?(dp[i-dp[i-1]-2])+dp[i-1]+2:dp[i-1]+2;
					}
				}else{
					dp[i] = 2 + ((i-2)>=0?dp[i-2]:0);
				}
				max = Math.max(max, dp[i]);
			}
		}
		return max;
    }
}

53. 最大子序和

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

分析:连续的子数组和最大,注意是连续的

-21-34-121-54
-21-2435615

每当遍历到 i 位置的元素,其位置的元素我们给他设置为连续子数组的一部分,也就是说当前部分的最大值为nums[i]或者是nums[i]+dp[i-1],无论如何,当前位置的元素是包含在其内部的,也就是dp[i]的含义为在当前 i 位置形成的最大值(包含当前位置),然后在每个位置取最大,就得到了结果
f ( i ) = { M a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) f(i)=\begin{cases} Max(dp[i-1]+nums[i],nums[i])\end{cases} f(i)={Max(dp[i1]+nums[i],nums[i])

public int maxSubArray(int[] nums) {
        int n = nums.length;
        int dp[] = new int[n];
        int res = nums[0];
        for(int i = 0;i < n;i++){
            dp[i] = i==0?nums[0]:Math.max(nums[i],dp[i-1]+nums[i]);
            res = Math.max(res,dp[i]);
        }
        return res;
    }

62. 不同路径

示例

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
起点1
12
1终点(3)

分析:当我们到达终点时,来源只有左和上,所以实际结果也就是dp[i] [j] = dp[i] [j-1] + dp[i-1] [j]
d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j ] dp[i] [j] = dp[i] [j-1] + dp[i-1] [j] dp[i][j]=dp[i][j1]+dp[i1][j]

public static int uniquePaths(int m, int n) {
		int dp[][] = new int[n+1][m+1];
		for(int i = 1;i <= n;i++) {
			for(int j = 1;j <= m;j++) {
				if(i==1&&j==1) {
					dp[i][j] = 1;
					continue;
				}
				dp[i][j] = dp[i-1][j] + dp[i][j-1];
			}
		}
		return dp[n][m];
    }
63. 不同路径 II

示例 1:

输入:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

这一题与上面一题的解法思路是一样的。在遇到障碍的时候,我们直接给其置为0即可

class Solution {
    public static int uniquePathsWithObstacles(int[][] obstacleGrid) {
		int m = obstacleGrid.length;
		int n = obstacleGrid[0].length;
        if (m == 1&&n==1&&obstacleGrid[0][0]==0){//只有一个
            return 1;
        }
        if (obstacleGrid[m-1][n-1] == 1 || obstacleGrid[0][0] ==1){//出入口堵死了
            return 0;
        }
        if (m == 1&&n==1 && obstacleGrid[0][0] == 1){//只有一个
            return 0;
        }
		int dp[][] = new int[m+1][n+1];
        if (m>=2) {
            dp[2][0] = 1;
        }
        if (n>=2) {
            dp[0][2] = 1;
        }
		for(int i = 1;i <= m;i++) {
			for(int j = 1;j <= n;j++) {
				if(obstacleGrid[i-1][j-1] == 1) {
					dp[i][j] = 0;
					continue;
				}
				dp[i][j] = dp[i-1][j] + dp[i][j-1];
			}
		}
		return dp[m][n];
    }
}

64. 最小路径和

示例:

输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

分析:

131
151
421

每在一个位置,其产生最小的值有两个来源,一个是其左边,一个是其上边,最小的产生为当前位置的值加上左,和当前位置加上上边的两者其中较小的一个
d p [ i ] [ j ] = m i n ( g r i d [ i ] [ j ] + d p [ i − 1 ] [ j ] , g r i d [ i ] [ j ] + d p [ i ] [ j − 1 ] ) dp[i][j] = min(grid[i][j]+dp[i-1][j],grid[i][j]+dp[i][j-1]) dp[i][j]=min(grid[i][j]+dp[i1][j],grid[i][j]+dp[i][j1])

class Solution {
   public static int minPathSum(int[][] grid) {
		int m = grid.length;
		int n = grid[0].length;
		int dp[][] = new int[m+1][n+1];
		for(int i = 1;i <= m;i++) {
			for(int j = 1;j <= n;j++) {
				if(i==1&&j==1) {
					dp[i][j] = grid[i-1][j-1];
					continue;
				}
				if(i==1) {
					dp[i][j] = grid[i-1][j-1] + dp[i][j-1];//第一行不考虑上
				}else if(j == 1) {
					dp[i][j] = grid[i-1][j-1] + dp[i-1][j];//第一列不考虑左
				}else {
					dp[i][j] = Math.min(dp[i-1][j]+grid[i-1][j-1], dp[i][j-1]+grid[i-1][j-1]);
				}
			}
		}
		return dp[m][n];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值