区间型动态规划,都是dp[i][j]由小的区间先计算,然后计算大的区间得到,模板就是:
for(int len = 2; len <= n; len++) {
for(int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
很多题都可以用这个模板写;
Longest Palindromic Subsequence 思路:区间型动态规划,f[i][j] 表示s[i] ~ s[j]之间最大的palindromic subsequence,递推公式是:设dp[i][j]表示第i到第j个字符间的最长回文序列的长度(i<=j)
状态初始条件:dp[i][i]=1 (i=0:n-1)
dp[i][j]=dp[i+1][j-1] + 2 if(str[i]==str[j])
dp[i][j]=max(dp[i+1][j],dp[i][j-1]) if (str[i]!=str[j])
class Solution {
public int longestPalindromeSubseq(String s) {
if(s == null || s.length() == 0) {
return 0;
}
int n = s.length();
int[][] dp = new int[n][n];
int maxlen = 0;
for(int i = 0; i < n; i++) {
dp[i][i] = 1;
maxlen = 1;
}
char[] ss = s.toCharArray();
for(int len = 2; len <= n; len++) {
for(int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if(ss[i] == ss[j]) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
maxlen = Math.max(maxlen, dp[i][j]);
}
}
return maxlen;
}
}
dp[i][j] 表示的物理意义是:string s里面i到j之间的string是否是palindrome. 那么很自然的会推导出递推关系:
if s(i) != s(j) 直接 dp[i][j] = false;
if s(i) == s(j) :
if( j-i<=2) dp[i][j] = true; // aba, 这个是容易犯错的点。 j-i = 2的情况一定要考虑进去;
else dp[i][j] = dp[i+1][j-1] 代表i,j指针向中间移动;
因为计算dp[i][j] 需要用到下一层的dp[i+1][j-1],所以for循环的时候,要从下开始算,也就是从后面开始计算;
class Solution {
public String longestPalindrome(String s) {
if(s == null || s.length() == 0) {
return s;
}
int n = s.length();
boolean[][] dp = new boolean[n][n];
String maxstr = "";
int maxlen = 1;
for(int i = 0; i < n; i++) {
dp[i][i] = true;
maxstr = s.substring(i, i + 1);
}
char[] ss = s.toCharArray();
for(int len = 2; len <= n; len++) {
for(int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if(ss[i] != ss[j]) {
dp[i][j] = false;
} else {
if(j - i == 1) {
dp[i][j] = true;
}
if(j - i >= 2 && dp[i + 1][j - 1]) {
dp[i][j] = true;
}
if(dp[i][j]) {
if(j - i + 1 > maxlen) {
maxlen = j - i + 1;
maxstr = s.substring(i, j + 1);
}
}
}
}
}
return maxstr;
}
}
思路:区间型动态规划 dp[i][j] = true if s[i] == s[j] and ( j - i <= 2 || dp[i+1][j - 1])
区间型,从小到大,计算,初值len = 1, len >= 2;
class Solution {
public int countSubstrings(String s) {
if(s == null || s.length() == 0) {
return 0;
}
int n = s.length();
boolean[][] dp = new boolean[n][n];
int pcount = 0;
for(int i = 0; i < n; i++) {
dp[i][i] = true;
pcount++;
}
char[] ss = s.toCharArray();
for(int len = 2; len <= n; len++) {
for(int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if(ss[i] != ss[j]) {
dp[i][j] = false;
} else {
if(j - i == 1) {
dp[i][j] = true;
pcount++;
}
if(j - i >= 2 && dp[i + 1][j - 1]) {
dp[i][j] = true;
pcount++;
}
}
}
}
return pcount;
}
}
Burst Balloons 思路:f[i][j]代表:i个气球和j个气球不能被扎破的情况下,中间扎破能够得到的最大值。
记住首尾先要加入一个1,然后区间型动态规划,一定是先计算小区间然后计算大区间,所以for循环用len来写,然后枚举起点,最后得到整个区间的最大值;
class Solution {
public int maxCoins(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
int n = nums.length;
int[] A = new int[n + 2];
// 最左边和最右边放值1;
A[0] = 1; A[A.length - 1] = 1;
n += 2;
for(int i = 1; i < n - 1; i++) {
A[i] = nums[i - 1];
}
int[][] dp = new int[n][n];
//最短的balloon之间是不能扎破的,所以为0;
for(int i = 0; i < n - 1; i++) {
dp[i][i + 1] = 0;
}
// [1] A[0]....A[n - 2] [1]
// 枚举length,区间型动态规划,都是从length从小到大计算的;
for(int len = 2; len <= n; len++) {
for(int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
for(int k = i + 1; k < j; k++) {
dp[i][j] = Math.max(dp[i][j],
dp[i][k] + dp[k][j] + A[i] * A[k] * A[j]);
}
}
}
return dp[0][n - 1];
}
}