动态规划刷题笔记

记录DataWhale 相对比较随意

动态规划四步解题法

674. 最长连续递增序列

5. 最长回文子串

class Solution {
    public String longestPalindrome(String s) {
        if(s.length()<2) return s;
        
        int start=0;
        int end=0;
        char[] ch=s.toCharArray();
        boolean[][] dp=new boolean[ch.length][ch.length];
        for (int i = 0; i < ch.length; i++) {
            dp[i][i]=true;
        }//赋值子串[i,i]必为回文串
        
        int maxLen=1;
        for (int j = 1; j < ch.length; j++) {
            for (int i = 0; i < j; i++) {//计算顺序可以画格子 根据 画格子 j > i的前提下,i从小开始计算
                if(ch[i]==ch[j]){
                    if(j-i<3){
                        dp[i][j]=true;//aba  或 aa 都为回文子串
                    }else{
                        dp[i][j]=dp[i+1][j-1];
                    }
                }

                if(dp[i][j]){
                    if(j-i+1>maxLen){
                        maxLen=j-i+1;
                        start=i;
                        end=j;
                    }
                }

            }//forj end
        }//fori end
        return s.substring(start,end+1);
    }
}
class Solution {
    public String longestPalindrome(String s) {
    if (s == null || s.length() < 1) return "";

    int start = 0, end = 0;
    
    for (int i = 0; i < s.length(); i++) {
        int len1 = expandAroundCenter(s, i, i);
        int len2 = expandAroundCenter(s, i, i + 1);
        int len = Math.max(len1, len2);
        if (len > end - start) {
            start = i - (len - 1) / 2;//前半部分 精确值 偶数长度是(len-1)/2 奇数长度是(len-1)/2
            end = i + len / 2;//后半部分 精确值 偶数长度时是完整的len/2 奇数长度时是(len-1)/2
        }
    }
    return s.substring(start, end + 1);
}

private int expandAroundCenter(String s, int left, int right) {
//获取以该索引扩散得到的最长回文串长度
    int L = left, R = right;
    while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
        L--;
        R++;
    }
    return R - L - 1;
}

}

516. 最长回文子序列

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int longestPalindromeSubseq(String s) {
        if(s.length()<2) return s.length();

        char[] ch=s.toCharArray();
        int len=ch.length;
        int[][] dp=new int[len][len];

        for (int i = 0; i < len; i++) {
            dp[i][i]=1;
        }

        for (int i = len-2; i >=0; i--) {
            for (int j = i+1; j < len; j++) {

                if(ch[i]==ch[j]){
                    dp[i][j]=dp[i+1][j-1]+2;
                }else{
                    dp[i][j]=Math.max(dp[i+1][j],dp[i][j-1]);
                }
            }//forj end
        }//fori end
        return dp[0][len-1];
    }
}
//leetcode submit region end(Prohibit modification and deletion)

72. 编辑距离


/*
我们采用从未尾开始遍历word1和word2,
当word[i]等于word2[j]时,说明两者完全一样,所以i和j指针可以任何操作都不做,用状
态转移式子表示就是dp[i][j]=dp[i-1][j-1],也就是前一个状态和当前状态是一样的。
当 word1[i]和word2[j]不相等时,就需要对三个操作进行递归了,这里就需要仔细思考状态转
移方程的写法了
对于插入操作,当我们在word1中插入一个和word2一样的字符,那么word2就被匹配了,所以可
以直接表示为dp[i][j-1]+1
对于删除操作,直接表示为dp[i-1][j+1
对于替换操作,直接表示为dp[i-1][j-1]+1
所以状态转移方程可以写成mn(dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1)
*/
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public int minDistance(String word1, String word2) {
        int n1 = word1.length();
        int n2 = word2.length();
        int[][] dp = new int[n1 + 1][n2 + 1];
        // 第一行
        for (int j = 1; j <= n2; j++) dp[0][j] = dp[0][j - 1] + 1;
        // 第一列
        for (int i = 1; i <= n1; i++) dp[i][0] = dp[i - 1][0] + 1;

        for (int i = 1; i <= n1; i++) {
            for (int j = 1; j <= n2; 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 - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
            }
        }
        return dp[n1][n2];
    }
}
//leetcode submit region end(Prohibit modification and deletion)

198. 打家劫舍

class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        }
        int[] dp = new int[length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[length - 1];
    }
}

213. 打家劫舍 II

class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        if (nums.length == 1) {
            return nums[0];
        }
        return Math.max(robHelp(Arrays.copyOfRange(nums, 1, nums.length)),robHelp(Arrays.copyOfRange(nums, 0, nums.length-1)));
        //主要是在普通版本上排除掉首尾同时偷取的部分,那一开始 我们就分两种情况 再按照不相邻规则偷取 即可
    }
    public int robHelp(int[] nums) {//这部分普通版本搬运过来
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        }
        int[] dp = new int[length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[length - 1];
    }
}

车轮滚滚 时间

五分钟学算法的动态规划系列:

荐MIT的动态规划练习资料

比较优质的动态规划文章