题目
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
分析
因为求的是回文子串的个数,而单个字符构成的子串都可以认为是回文串,所以一个给定的字符串,其最少拥有len(str)个回文子串。同时,如果多个回文子串的字符和顺序完全相同,但是其开始位置或者结束位置不同,那么也不认为这些回文子串是同一个。
我最先想到的方法是暴力破解,即先定义一个判断一个字符串是否是回文串的函数,然后遍历字符串的所有子串逐个判断。该方法的时间复杂度为O(n!),时间性能和空间性能都不算优秀。
public int countSubstrings(String s) {
StringBuilder new_s = new StringBuilder(s);
int res = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j <= s.length() ; j++) {
if (isHui(s.substring(i,j))){
res++;
}
}
}
return res;
}
private boolean isHui(String s){
StringBuilder rev_s = new StringBuilder(s).reverse();
if (s.equals(rev_s.toString())){
return true;
}
return false;
}
为此,需要想出一个更加优化的算法来解决这一问题。可以从字符串的某一个字符位置处向外扩展,扩展的方式分别是奇数扩展和偶数扩展两种方式,即分别求奇数长度和偶数长度的回文子串。这样就能大大缩短查询时间,时间复杂度为O(nlogn)。
private int cnt = 0;
public int countSubstrings(String s) {
for (int i = 0; i < s.length(); i++) {
extendSubstrings(s, i, i); // 奇数长度
extendSubstrings(s, i, i + 1); // 偶数长度
}
return cnt;
}
private void extendSubstrings(String s, int start, int end) {
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
start--;
end++;
cnt++;
}
}
参考:
https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode 题解 - 字符串.md#3-字符串中单词的翻转