本题要求回文子串,自己想到的解法是记录前一位字符串的回文子串次数,然后判断当前字符串的回文子串次数,时间复杂度高达O(n^3);
class Solution {
public:
bool isPalindrome(const string& s, int start, int end){
for (int i = start, j = end; i < j; i++, j--) {
if (s[i] != s[j]) {
return false;
}
}
return true;
}
int countSubstrings(string s) {
vector<int> dp(s.size(), 1);
for(int i = 0; i < s.size(); i++){
for(int start = 0; start < i; start++){
if(isPalindrome(s, start, i)){
dp[i]++;
}
}
if(i > 0){
dp[i] += dp[i - 1];
}
}
return dp[s.size() - 1];
}
};
学习了代码随想录 动态规划解法,在定义dp数组时,不是定义成字符串前 i 个字符回文子串的个数,而是布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
- 布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
- 当s[i]与s[j]不相等,那没啥好说的了,dp[i][j]一定是false。
- 当s[i]与s[j]相等时,这就复杂一些了,有如下三种情况
情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串
情况二:下标i 与 j相差为1,例如aa,也是回文子串
情况三:下标:i 与 j相差大于1的时候,例如cabac,此时s[i]与s[j]已经相同了,我们看i到j区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1 与 j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true。 - dp[i][j]初始化为false
- 从递推公式中可以看出,情况三是根据dp[i + 1][j - 1]是否为true,在对dp[i][j]进行赋值true的,dp[i + 1][j - 1] 在 dp[i][j]的左下角,所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的
class Solution {
public:
int countSubstrings(string s) {
vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false));
int num = 0;
for(int i = s.size() - 1; i >= 0; i--){
for(int j = i; j < s.size(); j++){
if(s[i] == s[j]){
if(j - i <= 1){
dp[i][j] = true;
num++;
}else if(dp[i + 1][j - 1]){
dp[i][j] = true;
num++;
}
}
}
}
return num;
}
};
注:本题最初使用size_t 类型的 i,j进行遍历,发现同样的代码,此时会超时,经过尝试,发现问题出现在了for(size_t i = s.size() - 1; i >= 0; i–) 这个逆序遍历这里,size_t 是一个无符号数,当为 0 时,size_t-- 将其变为了最大的无符号数,从而变成了一个死循环