代码随想录算法训练营Day51|动态规划part13-回文

86 阅读3分钟

LeetCode 647 回文子串

题目链接:leetcode.cn/problems/pa…

文档讲解:programmercarl.com/0647.回文子串.h…

视频讲解:www.bilibili.com/video/BV17G…

思路

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

考虑动态规划五部曲:

  1. dp数组及其下标含义:为了方便找到递归关系,考虑回文串的性质有:如果串s[i+1, j-1]是回文串,当s[i]==s[j]s[i, j]也是回文串。有i,j两个维度,所以需要定义一个二维dp数组。那么dp[i][j]的含义就是,s[i,j]是否是回文串。最后统计数组中有几个回文串就可以得到返回结果。
  2. 确定递推公式:
    1. if (s[i]==s[j]) dp[i][j] = dp[i+1][j-1]如果j-i <= 1,直接等于true。
    2. if (s[i]!=s[j]) dp[i][j] = false
  3. dp数组如何初始化:根据递推公式和定义,dp[i][j]以赖于左下角的元素,而i>j的元素是没有意义的。所以初始化所有i==j的元素
  4. 确定遍历顺序:为了让左下角一直先被填写。需要从左到右,从下到上遍历。所以i的初始值以赖于j,把j放在外层,i放在内层。
  5. 举例推导dp数组

解法

class Solution {
	public int countSubstrings(String s) {	
		boolean[][] dp = new boolean[s.length()][s.length()];		
		for (int i = 0; i < s.length(); i++) {		
			dp[i][i] = true;		
		}		
		int count = s.length();		
		for (int j = 0; j < s.length(); j++) {		
			for (int i = j - 1; i >= 0; i--) {			
				if (s.charAt(i) == s.charAt(j)) {				
					if (j - i <= 1) {					
						dp[i][j] = true;					
					}					
					else {					
						dp[i][j] = dp[i+1][j-1];					
					}				
				}				
				else {				
					dp[i][j] = false;				
				}				
				if (dp[i][j]) {				
					count++;					
				}			
			}			
		}		
		return count;	
	}
}

LeetCode 516 最长回文子序列

题目链接:leetcode.cn/problems/lo…

文档讲解:programmercarl.com/0516.最长回文子序…

视频讲解:www.bilibili.com/video/BV1d8…

思路

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

本题找的是回文子序列。由于不要求连续,我们可以把目标返回值作为数组的定义。

考虑动态规划五部曲:

  1. dp数组及其下标含义:dp[i][j]表示s[i,j]子串中最长回文子序列的长度
  2. 确定递推公式:
    1. if (s[i]==s[j]) dp[i][j] = dp[i+1][j-1] + 2.如果j - i <= 1dp[i][j] = j - i + 1,即s[i, j]的长度
    2. if (s[i]!=s[j]) dp[i][j] = max(dp[i][j-1], dp[i-1][j])
  3. dp数组如何初始化:都初始化为0
  4. 确定遍历顺序:根据递推公式,一个元素依赖于其左下角的元素,左边的和下面的元素。所以需要从左到右,从下到上遍历。
  5. 举例推导dp数组

解法

class Solution {
	public int longestPalindromeSubseq(String s) {	
		int[][] dp = new int[s.length()][s.length()];		
		for (int j = 0; j < s.length(); j++) {		
			for (int i = j; i >= 0; i--) {			
				if (s.charAt(i)==s.charAt(j)) {				
					if (j - i > 1) {					
						dp[i][j] = dp[i+1][j-1] + 2;					
					}					
					else {					
						dp[i][j] = j - i + 1;					
					}				
				}				
				else {				
					dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]);				
				}			
			}		
		}		
		return dp[0][s.length()-1];	
	}
}

动态规划总结

动态规划五部曲

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

动态规划基础

主要围绕动态规划的基础方法论

背包问题

背包问题.png

股票问题

股票问题.png

打家劫舍问题

打家劫舍问题.png

子序列问题

子序列问题.png

今日收获总结

今日学习三小时。总结见动态规划大总结。