这个题我写的有点麻烦,又是在string下标上调了半天。感觉做这种编程题尽量还是避免这种复杂的下标变换,真的很容易出错。我的思路是,定义一个vector<int> dp(n, 0),从后往前遍历字符串,比如,遍历字符串第i个位置s[i],只需要求以s[i]开头的回文子串个数ni。那么s[i, s.size()-1]这个子串的所有回文子串就是ni + dp[i + 1]+...+dp[n]。动态规划就出来了。
class Solution {
public:
int countSubstrings(string s) {
vector<int> dp(s.size(), 0);
dp[s.size() - 1] = 1;
for(int i = s.size() - 2; i >= 0; --i)
{
dp[i] = dp[i + 1] + palincount(s.substr(i, s.size() - i));
}
return dp[0];
}
int palincount(string s) //计算以s[0]开头的回文子串有几个,遍历一遍即可
{
int count = 0;
for(int i = 1; i <= s.size(); ++i)
{
if(ispalin(s.substr(0, i)))
{
count++;
}
}
return count;
}
bool ispalin(string s) //判断一个字符串是不是回文
{
int start = 0, end = s.size() - 1;
while(start < end)
{
if(s[start] != s[end])
return false;
start ++; //这里千万不能忘!
end--; //这里千万不能忘!
}
return true;
}
};
但是这个题考察的绝对不是这样。这个题的思路是不局限于一维dp,而是用二维dp:class Solution {
public:
int countSubstrings(string s) {
int n = s.size(), count = 0;
vector<vector<int>> dp(n, vector<int> (n));
for ( int end = 0; end < n; ++end ) {
dp[end][end] = 1;
++count;
for ( int start = 0; start < end; ++start ) {
if ( s[start] == s[end] && (start+1 >= end-1 || dp[start+1][end-1])) {
dp[start][end] = 1;
++count;
}
}
}
return count;
}
};
定义d[i][j]:若从i到j的字符串为回文,则为真(1),否则为假(0),那么d[i][j]为真的前提是:头尾两个字符串相同并且去掉头尾以后的字串也是回文(即dp[start+1][end-1]为真),这里面要注意特殊情况,即:去掉头尾以后为空串,也是回文,比如"aa"