1 题目
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
示例 1:
输入: “abc”
输出: 3
解释: 三个回文子串: “a”, “b”, “c”.
示例 2:
输入: “aaa”
输出: 6
说明: 6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”.
注意:
输入的字符串长度不会超过1000。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/palindromic-substrings
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2 Java
2.1 !方法一(中心扩展,思维)
遍历每个 / 每两个字符,作为回文字符串的中心
不断扩展回文字符串,并记录回文字符串数量
class Solution {
public int countSubstrings(String s) {
int ans = 0, N = s.length();
// “abc”的中心位置可以是a,也可以是ab,所以存在两个字符的中心
// l和r为中心的范围,i是为了应对两字符中心的情况
for(int i = 0; i <= 2 * N - 2; i++){
int l = i / 2, r = l + i % 2;
while(l >= 0 && r < N && s.charAt(l) == s.charAt(r)){
l--; r++;
ans++;
}
}
return ans;
}
}
2.2 !!!方法二(动归乱序迭代)
dp[i][j]:代表[i, j]是否为回文字符串
状态转移方程:回文字符串两段加同样字符,仍为回文字符串
向前步进:一定注意for步进的方向,与状态转移方程有关,必须由之前的dp得到之后的dp!!!
本题dp[i][j]由dp[i + 1][j - 1]得到,所以i在步进,j在步退
class Solution {
public int countSubstrings(String s) {
int ans = 0, N = s.length();
// 创建备忘录;代表[i, j]是否为回文字符串
boolean[][] dp = new boolean[N][N];
// 状态转移方程;回文字符串两段加同样字符,仍为回文字符串
// 向前步进;一定注意for步进的顺序,与状态转移方程有关,必须由之前的dp得到之后的dp!!!
for(int j = 0; j < N; j++){
for(int i = j; i >= 0; i--){
if(s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])){
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
}
经典错误,两个for循环的步进与状态转移方程不匹配
class Solution {
public int countSubstrings(String s) {
int ans = 0, N = s.length();
// 创建备忘录;代表[i, j]是否为回文字符串
boolean[][] dp = new boolean[N][N];
// 状态转移方程:回文字符串两段加同样字符,仍为回文字符串
// 向前步进;一定注意for步进的顺序,与状态转移方程有关,必须由之前的dp得到之后的dp!!!
// !!!经典错误,两个for循环的步进与状态转移方程不匹配
for(int i = 0; i < N; i++){
for(int j = i; j < N; j++){
if(s.charAt(i) == s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])){
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
}