1.回文子串
这个题目让我们找回文子串的个数,这里不要把子串和子序列的概念搞错
1.首先我们先确定状态表示(不要问我怎么来的,大多和贪心一样是经验和感觉)
如果我们用 一维的状态表示,可以记录以某一位置为结尾的回文串但是并不能确定这一回文串从哪里开始,这并不是我们希望的,所以我们采用二维的状态表示dp[i][j],它表示以i开始以j结尾的回文串。
2.推导状态转移方程
一. 对于dp[i][j]如果s[i] == s[j]那么在i+1 < j的情况下必然有dp[i][j] = dp[i+1][j-1];
二.如果s[i] == s[j]当i+1 == j时显然i和j的位置是相邻的 必然要dp[i][j] = true;
三.如果s[i] == s[j]当i == j显然i和j重合必然有dp[i][j] = true;
四.如果s[i] != s[j]那这个区间必然不是回文串所以不用考虑
3.初始化
因为我们的状态转移方程用到了dp[i+1][j-1]所以我们的填表顺序是从下往上,从左往右,如果不明白,就看看下面这个图,因为我们的状态转移方程很全所以其实可以不用初始化,但是编译器会自动初始化为false也不影响,只要注意填表顺序就可以了。
到这里整体的思路就讲完了,可以先去谢谢再来看我的解答
int countSubstrings(string s) {
int len = s.size();int ret = 0;
vector<vector<bool>> dp(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j]) dp[i][j] = i+1 < j ? dp[i+1][j-1] : true;
if(dp[i][j]) ret++;
}
return ret;
}
2.最长回文子串
这道题的思路是建立在上面的题目之上的,在上一题我们已经能够判断那个区间的子字符串是回文串
现在让我们找最长的其实可以通过一个_max来记录当前的最大长度,在通过lefft和right来记录这个区间的左端点和右端点,两层for循环来遍历不断更新left和right如果j-i+1大于_max那么我们需要更新左右端点和_max直到循环接收找到我们的最大区间
然后通过left和right来输出相应的字符串
可以先去写写再来看题解
string longestPalindrome(string s)
{
int len = s.size(); int max = 0,left,right;
vector<vector<bool>> dp(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
{
if(s[i] == s[j]) dp[i][j] = i+1 < j ? dp[i+1][j-1] : true;
if(dp[i][j])
{
if(max < j-i+1)
{
max = j-i+1;
left = i;right = j;
}
}
}
string rets;
for(int i = left;i<=right;i++)
rets+=s[i];
return rets;
}
3.分割回文串IV
同样是建立在第一题的基础上,因为本题的要求是将字符串分成三段,那我们可以理解为对这个字符串砍两刀,如果砍两刀后的三个区间都是回文字符串那就是true,这两刀就是两层for循环显然如果从0开始找第一刀的位置是没有意义的所以我们从1开始找,第二道的位置显然在第一刀之后并且不与i重合。
思路就讲完了,可以先去写写再来看我的
bool checkPartitioning(string s)
{
int len = s.size();
vector<vector<bool>> dp(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
if(s[i] == s[j]) dp[i][j] = i+1 < j ? dp[i+1][j-1] : true;
for(int i = 1;i<len;i++)
for(int j = i+1;j<len;j++)
if(dp[0][i-1] && dp[i][j-1]&&dp[j][len-1]) return true;
return false;
}
4.分割字符串II
这个题同样是在第一题的基础上做的拓展,我们在知道那个区间是回文串的基础上,可以可以重新定一个状态表示以i位置为结尾的字符串最少需要切几刀
状态转移方程:
如果我们的0到i位置是回文串那么dp[i] = 0因为他既然是回文串我们就不需要切割
如果0到i位置不是回文串,那么我们就要从i位置开始往前找一个j直到j到i是回文串,然后我们需要判断dp[j]+1和原来的dp[i]那个小如果dp[j]+1小那么将dp[i]改为dp[j]+1.
初始化:可以全部初始化为0
可以自己先写一下再看我的解答
int minCut(string s)
{
int len = s.size();
vector<vector<bool>> isLM(len,vector<bool>(len));
for(int i = len-1;i>=0;i--)
for(int j = i;j<len;j++)
if(s[i] == s[j]) isLM[i][j] = i+1 < j ? isLM[i+1][j-1] : true;
int ret = 0;
vector<int> dp(len,INT_MAX);
for(int i = 0;i<len;i++)
if(isLM[0][i]) dp[i] = 0;
else{
for(int j = i;j>=0;j--)
if(isLM[j][i]) dp[i] = min(dp[i],dp[j-1]+1);
}
return dp[len-1];
}