回文串问题
回文串算是比较经典的算法题了,回文串的概念是比较好理解的,但是解题逻辑比较难想。
我们不能再按照题目问什么,我们就把dp数组设置成什么了。因为我们如果把dp数组设置成回文子串 的数目。则发现,递推公式是没办法写出来的,当前数组和其他数组之间的关系是很难找到的,所以本题重点在于dp数组的定义。
-
确定dp数组(dp table)以及下标的含义:
我们在判断字符串S是否是回文,那么如果我们知道 s[1],s[2],s[3] 这个子串是回文的,那么只需要比较 s[0]和s[4]这两个元素是否相同,如果相同的话,这个字符串s 就是回文串。
那么此时我们是不是能找到一种递归关系,也就是判断一个子字符串(字符串的下表范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。
所以我们把dp数组定义为布尔类型,布尔类型的
dp[i][j]
:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]
为true,否则为false。 -
递推公式
-
s[i]与s[j]不相等:
dp[i][j]
一定是false。 -
s[i]与s[j]相等:
-
下标i 与 j相同,同一个字符例如a,当然是回文子串
-
下标i 与 j相差为1,例如aa,也是回文子串
-
下标:i 与 j相差大于1的时候,这个区间是不是回文就看
dp[i + 1][j - 1]
是否为true。
-
if(s.charAt(i) == s.charAt(j)) { if(j-i <= 1) {//情况1和情况2 res++; dp[i][j] = true; } else if(dp[i+1][j-1]) { res++; dp[i][j] = true; } }
-
-
初始化:全部初始化为false,或者把对角线全部初始化为true,这样递推的时候就可以少判断一个情况。
-
遍历顺序:根据递推公式
dp[i+1][j-1]
可知:一定要从下到上,从左到右遍历,这样保证dpi + 1都是经过计算的。
class Solution { public int countSubstrings(String s) { boolean[][] dp = new boolean[s.length()][s.length()];//左闭右闭区间 int res = 0; //从下往上,从左到右,不遍历 j<i的情况 for(int i = s.length()-1; i >= 0; i--) { for(int j = i; j < s.length(); j++) { if(s.charAt(i) == s.charAt(j)) { if(j-i <= 1) {//情况1和情况2 res++; dp[i][j] = true; } else if(dp[i+1][j-1]) { res++; dp[i][j] = true; } } } } return res; } }
首先,我们要明确本题的子序列和上一题的子串有什么区别:字串是连续的,子序列可以不连续。
上一题和本题的思路是差不多的,我们用动规五部曲分析一下。
-
dp数组初始化:
本题需要计算数量,用boolean类型来定义就不太合适了。
dp[i][j]
:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]
。 -
递推公式
-
如果s[i]与s[j]相同,那么
dp[i][j] = dp[i + 1][j - 1] + 2;
-
如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。
加入s[j]的回文子序列长度为
dp[i + 1][j]
。加入s[i]的回文子序列长度为
dp[i][j - 1]
。那么
dp[i][j]
一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
-
-
初始化
对角线初始化为1,可以不用考虑 j==i的情况。
-
遍历顺序:从下往上,从左往右。
class Solution { public int longestPalindromeSubseq(String s) { int[][] dp = new int[s.length()][s.length()]; for (int i = 0; i < s.length(); i++) { dp[i][i] = 1; } for(int i = s.length()-2; i>=0; i--) { for(int j = i+1; j < s.length(); j++) { if (s.charAt(i) == s.charAt(j)) { dp[i][j] = dp[i+1][j-1] + 2; }else { dp[i][j] = Math.max(dp[i+1][j], dp[i][j-1]); } } } return dp[0][s.length()-1]; } }