
ps:图片源于网络,侵删
目录
一、题目
给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
提示:
1 <= s.length <= 1000s由小写英文字母组成
二、代码
java
class Solution {
/**
* 计算字符串中回文子串的总数
* @param s 输入的字符串
* @return 回文子串的数量
*/
public int countSubstrings(String s) {
// 边界条件处理:如果字符串为空或长度为0,直接返回0
if (s == null || s.length() == 0) {
return 0;
}
int count = 0; // 初始化回文子串的计数器
// 遍历字符串的每一个字符,将其作为潜在的回文中心
for (int i = 0; i < s.length(); ++i) {
// 情况1:以当前字符 s[i] 为单一中心(奇数长度回文,如 "aba")
// 传入相同的索引 i, i 作为左右起点
count += countPalindrome(s, i, i);
// 情况2:以当前字符 s[i] 和下一个字符 s[i+1] 为双中心(偶数长度回文,如 "abba")
// 传入相邻的索引 i, i+1 作为左右起点
count += countPalindrome(s, i, i + 1);
}
return count; // 返回最终的统计结果
}
/**
* 从指定的中心位置向两边扩展,统计能形成的回文子串数量
* @param s 输入的字符串
* @param start 扩展的左边界起始索引
* @param end 扩展的右边界起始索引
* @return 以该中心扩展出的回文子串数量
*/
private int countPalindrome(String s, int start, int end) {
int count = 0; // 记录当前中心能找到的回文串个数
// 循环条件:
// 1. start >= 0 : 左边界不能越界(不能小于0)
// 2. end < s.length() : 右边界不能越界(不能大于等于字符串长度)
// 3. s.charAt(start) == s.charAt(end) : 左右两边的字符必须相等
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
// 只要满足上述条件,说明找到了一个新的回文子串
count++;
// 继续向两边扩展:左指针左移,右指针右移
start--;
end++;
}
return count; // 返回当前中心找到的回文子串总数
}
}
三、核心逻辑解析
- 为什么循环里要调用两次
countPalindrome?
因为回文串的长度分为奇数和偶数。- 奇数回文(如
"racecar")有一个绝对的中心字符,所以用(i, i)扩展。 - 偶数回文(如
"noon")的中心在两个字符之间,所以用(i, i+1)扩展。
- 奇数回文(如
- 时间复杂度 : O(N2)O(N2) 。外层循环遍历 NN 个字符,内层
while循环在最坏情况下(比如全由相同字符组成的字符串"aaaaa")会扩展到边界,耗时 O(N)O(N) 。 - 空间复杂度: O(1)O(1) 。只使用了几个整型变量,没有使用额外的数组或哈希表。