题目链接:
题目描述
1 暴力
复杂度分析
时间复杂度:O(n^3)
空间复杂度:O(1)
class Solution {
public:
int countSubstrings(string s) {
if(s.empty()) return 0;
int ret = 0;
for(int i = 0; i<s.size(); ++i){
for(int j = i; j<s.size(); ++j){
if (isPalindrome(s,i,j))
++ ret;
}
}
return ret;
}
// 判断一个字符串是否为回文串
bool isPalindrome(string s, int i, int j){
while(i<=j){
if(s[i++]!=s[j--])
return false;
}
return true;
}
};
2 从中心字符扩展
一个回文串的中心字符位置是固定的,可以用回文串的中心位置+长度来唯一表示一个回文串
以某一位字符串为中心,向两边扩展;
分别以奇数和偶数长度统计回文串数目
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
int countSubstrings(string s) {
if(s.empty()) return 0;
int ret = 0;
for(int i = 0; i<s.size(); ++i){
count(s,i,i,ret); // 以i位置字符为中心,长度为奇数的回文串数目
count(s,i,i+1,ret); // 以i,i+1位置字符为中心,长度为偶数的回文串数目
}
return ret;
}
void count(string &s, int begin, int end, int &ret){
while (begin>=0 && end<s.size() && s[begin--] == s[end++])
++ ret;
}
};
3 动态规划
二维数组dp[i][j]
表示字符串s(i,j)
是否为回文串。
(1)首先需要初始化长度为1,2的字符串其dp值
(2)对于长度3~len
dp[i][j] = (s[i] == s[j] && dp[i+1][j-1])
需要注意的是,上式在遍历时不是从前往后遍历的,dp[i][j]依赖于dp[i+1][j-1]。所以坐标需要做处理,使得整个遍历过程是从前往后的。
因为前面已经求得了长度为1,2的字符串dp值,所以我们从i = 3~len
开始遍历子串长度,并检查 j = 0~len-i+1
为起点,end = j + i -1
为终点的子串是否为回文子串。
dp[j][end] = (s[j] == s[end] && dp[j+1][end-1])
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n^2)
/*
* 动态规划
* 时间复杂度O(n^2) 空间复杂度O(n^2)
*/
class Solution {
public:
int countSubstrings(string s) {
if(s.empty()) return 0;
int len = s.size();
vector<vector<bool >> dp(len, vector<bool > (len, false));
int ret = 0;
// 首先处理长度为1和2的子串
for (int i = 0; i < len; ++i) {
dp[i][i] = true; // 初始化长度为1的回文串
++ ret;
}
for (int i = 0; i< len -1; ++i){
if (s[i] == s[i+1]){
dp[i][i+1] = true; // 初始化长度为2的回文串
++ ret;
}
}
// i为遍历的子串长度,j为起始节点
for (int i = 3; i <= len ; ++i) {
for (int j = 0; j <= len -i + 1; ++j) {
int end = j+i-1;
if(s[j] == s[end] && dp[j+1][end-1]){
dp[j][end] = true;
++ ret;
}
}
}
return ret;
}
};