题目描述:
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:“abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”
示例 2:
输入:“aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”
提示:
输入的字符串长度不会超过 1000 。
代码:
public class LC647 {
//中心扩展,时间复杂度O(n^2) 空间复杂度O(1) 不如dp好理解
public int countSubstrings(String s) {
int len = s.length();
int ans = 0;
//长度为n的字符串会生成2n-1组回文中心
//我们只需要将这些回文中心遍历一遍,然后把奇数长度和偶数长度两种情况统一起来即可
for (int i = 0; i < 2 * len - 1; i++){
int l = i / 2,r = i / 2 + i%2;
while (l >=0 && r < len && s.charAt(l) == s.charAt(r)){
--l;
++r;
++ans;
}
}
return ans;
}
//dp,时间复杂度O(n^2) 空间复杂度O(n^2)
public int countSubstrings1(String s){
//用来存取该字符串中的区间为[i,j]的子串是否是回文串,是的话为true,不是为false
//那么我们最后只需要遍历其全部有可能的区间,然后判断其为false还是true即可
//最后统计dp数组中true的个数,即就是我们要找的回文子串个数
boolean[][] dp = new boolean[s.length() + 10][s.length() + 10];
int ans = 0;
//为什么这里我们第一层循环要倒着呢?
//因为我们的状态转移方程为:dp[i][j] = j-i<=2 || dp[i + 1][j - 1]
//如果正着来,我们求dp[i][j]的时候就需要知道dp[i + 1][j - 1],但此时dp[i + 1][j - 1]的结果我们还没有得到
for (int i = s.length() - 1; i >= 0; i--){
for (int j = i; j < s.length(); j++){
//区间左右端点不同,一定不是回文
if (s.charAt(i) != s.charAt(j))
continue;
//动态转移方程
if (j - i <= 2 || dp[i + 1][j - 1]){
dp[i][j] = true;
}
//统计是回文的个数
if (dp[i][j]){
ans++;
}
}
}
return ans;
}
public static void main(String[] args) {
LC647 obj = new LC647();
System.out.println(obj.countSubstrings1("abc"));
}
}