题目描述
解题思路
dp
- 确定dp含义
 
布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
- 递推公式
 
当i索引的字符和j索引的字符相同时,i,j区间是否为回文串的几种情况:
- i==j时,例如a,是回文串
 - j - i == 1 时,例如aa,是回文串
 - 如果j - i > 1,此时需要根据i + 1 和 j - 1是否为回文字串推出,所以dp[i][j] = dp[i+1][j-1];
 
当i索引的字符和j索引的字符不相同时,不是回文串。
- dp数组如何初始化
 
dp[i][j]可以初始化为true么? 当然不行,怎能刚开始就全都匹配上了。
所以dp[i][j]初始化为false。
- 遍历顺序
 
首先从递推公式中可以看出,情况三是根据dp[i + 1][j - 1]是否为true,在对dp[i][j]进行赋值true的。
此时需要使用左下角的数才能推出右上角的数,所以一定注意是从下到上,从左到右遍历。
- 举例推导dp数组
 
举例,输入:”aaa”,dp[i][j]状态如下:
注意因为dp[i][j]的定义,所以j一定是大于等于i的,那么在填充dp[i][j]的时候一定是只填充右上半部分。
/*** 动态规划** @param s* @return*/public int countSubstrings(String s) {// 表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。boolean[][] dp = new boolean[s.length()][s.length()];char[] array = s.toCharArray();int result = 0; // 回文字串个数// 初始化,最开始一定不是回文字串for (int i = 0; i < s.length(); i++) {dp[0][i] = false;dp[i][0] = false;}// 从下到上,从左到右遍历for (int i = array.length - 1; i >= 0; i--) {for (int j = i; j < array.length; j++) {if (array[i] == array[j]) {if (j - i <= 1) { // 情况一:i=j,只有一个字母,a,为回文字串 情况二:两个字母,aa,也为回文子串result++;dp[i][j] = true;} else if (dp[i + 1][j - 1]) { // 情况三:此时大于三个字符,就需要判断中间的区间内是否为回文字串result++;dp[i][j] = true;}}}}return result;}
双指针法
大致思路:双指针如何比较回文数,从中间开始,向两边扩散,如果两边值相等,那么就是回文字串。此时,如何判断中心点,例如aabbaa,此时使用1作为中心点就会丢失部分回文串,所以应该使用2作为中心点,那为什么不使用3呢,因为3可以由1扩散得到。
首先确定回文串,就是找中心然后向两边扩散看是不是对称的就可以了。
在遍历中心点的时候,要注意中心点有两种情况。
一个元素可以作为中心点,两个元素也可以作为中心点。
那么有人同学问了,三个元素还可以做中心点呢。其实三个元素就可以由一个元素左右添加元素得到,四个元素则可以由两个元素左右添加元素得到。
/*** 双指针法** @param s* @return*/public int countSubstrings(String s) {// 首先确定回文串,就是找中心然后向两边扩散看是不是对称的就可以了。// 在遍历中心点的时候,要注意中心点有两种情况。// 一个元素可以作为中心点,两个元素也可以作为中心点。int result = 0;for (int i = 0; i < s.length(); i++) {result += extend(s, i, i, s.length()); // 以1个字符作为中心点result += extend(s, i, i + 1, s.length()); // 以2个字符作为中心点// 三个字符等于一个字符左右添加元素,所以奇数都可以由一个中心点推出来// 四个字符等于二个字符左右添加元素,所以偶数都可以由二个中心点推出来}return result;}/*** 循环扩散的判断是否为回文字串(向2边扩散,而不是收缩)** @param s* @param i* @param j* @param n* @return*/int extend(String s, int i, int j, int n) {int res = 0; // 回文串的个数while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) { // 扩散查找,如果不满足相等直接退出循环i--; // i向左j++; // j向右res++;}return res;}
