题目链接
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:
输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
此题,使用暴力的做法,显然就是枚举所有的子串,判断每个子串是不是回文串,这样的时间复杂度肯定超时。枚举就要算500,500次,加上判断是不是回文子串,时间复杂度肯定超过一秒了。
所有,我们需要研究一下回文串的规律,我们按回文串的长度来分析,怎样才能构成一个回文串。
当回文串长度为1时,显然任意字符都能构成回文串
当回文串长度为2时,显然两个字符必须相等才能构成回文串
当回文串长度为3时,显然两边的字符相等,中间字符任意,才能构成字符串
当回文串长度为4时,显然中间两个字符相等,两边字符也相等,才能构成回文串
当指定串长度为5时,显然(中间三个字符串,两边相等,中间任意),然后两边字符相等,才能构成回文串
分析到这,不难发现,
回文串长度为4的时候,中间字符串满足长度为2的回文串条件,加上两边字符相等就可以构成回文串
回文串长度为5的时候,中间字符串满足长度为3的回文串条件,加上两边字符串相等就可以构成回文串
此时,不难写出dp的动态转移方程
定义i为长度为n的字符串
定义j为当前访问到的子字符串位置
dp[i][j]为i长度的回文串到数组长度为j的位置有多少个
所有dp[i][j]的值有以下几种情况。定义字符串数组为s
当 s[j] == s[j-i]的时候
如果 i长度小于4
dp[i][j] = dp[i][j-1] + 1;
否则,需要判断中间是否为回文串,因为长度为4的时候,中间是两个字符,长度为5的时候,中间为3个字符,所有需要判断i-2的位置
即dp[i-2][j-1]-dp[i-2][j-2]!=0
不等于0证明是一个回文串
dp[i][j] = dp[i][j-1] + 1;
否则
dp[i][j] = dp[i][j-1] ;
当 s[j] != s[j-i]的时候,显然
dp[i][j] = dp[i][j-1] ;
代码如下:
public int countSubstrings(String s) {
int len = s.length();
int dp[][] = new int[len][len];
dp[0][0] = 1;
for (int i = 1; i < len; i++) {// 初始化长度为1的回文串
dp[0][i] = dp[0][i - 1] + 1;
}
for (int i = 1; i < len; i++) {
for (int j = i; j < len; j++) {
if ((i - 1) >= 2) {// 因为长度为1,2,3的回文串不需要判断中间字符串
if (dp[i - 2][j - 1] - dp[i - 2][j - 2] != 0) {
if (s.charAt(j) == s.charAt(j - i)) {
dp[i][j] = dp[i][j - 1] + 1;
} else {
dp[i][j] = dp[i][j - 1];
}
} else {
dp[i][j] = dp[i][j - 1];
}
} else {
if (s.charAt(j) == s.charAt(j - i)) {
dp[i][j] = dp[i][j - 1] + 1;
} else {
dp[i][j] = dp[i][j - 1];
}
}
}
}
/*如果还不是和理解,可以将这段代码解除注释,看一下运算过程,自己跟着算一遍,就可以深刻理解了
* for (int i = 0; i < len; i++) { System.out.print(String.format("%2c",
* s.charAt(i)) + " "); } System.out.println(); for (int i = 0; i < len; i++) {
* for (int j = 0; j < len; j++) { System.out.print(String.format("%2d",
* dp[i][j]) + " "); } System.out.println(); }
*/
int sum = 0;
for (int i = 0; i < len; i++) {//加上每个长度回文串的个数,即为答案
sum += dp[i][len - 1];
}
return sum;
}