动态规划专训6——回文串系列

动态规划题目中,常出现回文串相关问题,这里单独挑出来训练

1.回文子串

LCR 020. 回文子串

给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串

1.状态表示:用dp[ i ][ j ]表示以 i 开始,j 结尾的子串是否是回文串

2.状态转移方程:dp[ i ][ j ] = s[ i ] == [ j ] && dp[ i + 1 ][ j - 1 ] 

3.初始化:无需初始化

4.填表顺序:从下往上填

5.返回值:dp表中ture的个数

class Solution {
public:
    int countSubstrings(string s) 
    {
        int n = s.size();
        vector<vector<bool>> dp(n, vector<bool>(n));
        int ret = 0;
        for(int i = n - 1; i >= 0; --i)
            for(int j = i; j < n; ++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;
    }
};

这是ac代码

2.最长回文子串

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串

1.状态表示:用dp[ i ][ j ]表示以 i 开始,j 结尾的子串是否是回文串

2.状态转移方程:dp[ i ][ j ] = s[ i ] == [ j ] && dp[ i + 1 ][ j - 1 ] 

3.初始化:无需初始化

4.填表顺序:从下往上填

5.返回值:最长回文子串

class Solution {
public:
    string longestPalindrome(string s) 
    {
        int n = s.size();
        vector<vector<bool>> dp(n, vector<bool>(n));
        int begin = -1, len = 0;
        for(int i = n - 1; i >= 0; --i)
            for(int j = i; j < n; ++j)
                if(s[i] == s[j])
                {
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
                    if(dp[i][j] && j - i + 1 > len)
                        len = j - i + 1, begin = i; 
                }

        return s.substr(begin, len);
    }
};

这是ac代码

3.分割回文串IV

1745. 分割回文串 IV

给你一个字符串 s ,如果可以将它分割成三个 非空 回文子字符串,那么返回 true ,否则返回 false 。

当一个字符串正着读和反着读是一模一样的,就称其为 回文字符串

1.状态表示:用dp[ i ][ j ]表示以 i 开始,j 结尾的子串是否是回文串

2.状态转移方程:dp[ i ][ j ] = s[ i ] == [ j ] && dp[ i + 1 ][ j - 1 ] 

3.初始化:无需初始化

4.填表顺序:从下往上填

5.返回值:据题目

class Solution {
public:
    bool checkPartitioning(string s) 
    {
        int n = s.size();
        vector<vector<bool>> dp(n, vector<bool>(n));
        for(int i = n - 1;  i >= 0; --i)
            for(int j = i; j < n; ++j)
                if(s[i] == s[j])
                    dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;

        for(int i = 1; i <= n - 2; ++i)
            for(int j = i; j <= n - 2; ++j)
                if(dp[0][i - 1] && dp[i][j] && dp[j + 1][n - 1])
                    return true;

        return false;
    }
};

这是ac代码

4.分割回文串II

LCR 094. 分割回文串 II

给定一个字符串 s,请将 s 分割成一些子串,使每个子串都是回文串。

返回符合要求的 最少分割次数

1.状态表示:用dp[ i ]表示以 i 位结尾,是每个子串都是回文串的最少分割次数

2.状态转移方程:dp[ i ] = min(dp[ i ], dp[ j - 1 ] + 1);

3.初始化:全部初始化为INT_MAX

4.填表顺序:从左往右填

5.返回值:dp[ n  - 1]

class Solution {
public:
    int minCut(string s) 
    {
        int n = s.size();
        vector<vector<bool>> arr(n, vector<bool>(n));
        for(int i = n - 1; i >= 0; --i)
            for(int j = i; j < n; ++j)
                if(s[i] == s[j])
                    arr[i][j] = i + 1 < j ? arr[i + 1][j - 1] : true;

        vector<int> dp(n, INT_MAX);
        for(int i = 0; i < n; ++i)
        {
            if(arr[0][i]) dp[i] = 0;
            else 
            {
                for(int j = 0; j <= i; ++j)
                    if(arr[j][i])
                        dp[i] = min(dp[i], dp[j - 1] + 1);
            } 
        }

        return dp[n - 1];
    }
};

这是ac代码

5.最长回文子序列

516. 最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列

1.状态表示:用dp[ i ][ j ]表示以 i 开始,j 结尾的最长回文子序列的长度

2.状态转移方程:

                if (s[ i ] == s[ j ]) dp[ i ][ j ]  = 2 + dp[ i + 1 ][ j - 1 ] ;

                else dp[ i ][ j ] = max(dp[ i + 1 ][ j ], dp[ i ][ j - 1 ]);

3.初始化:无需初始化

4.填表顺序:每一行从上往下,每一列从左往右

5.返回值:dp[ 0 ][ n - 1 ]

class Solution {
public:
    int longestPalindromeSubseq(string s) 
    {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        for(int i = n - 1; i >= 0; --i)
        {
            dp[i][i] = 1;
            for(int j = i + 1; j < n; ++j)
            {
                if(s[i] == s[j]) dp[i][j] = 2 + dp[i + 1][j - 1];
                else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
            }
        }

        return dp[0][n - 1];
    }
};

这是ac代码

6.让字符串成为回文串的最少插入次数

1312. 让字符串成为回文串的最少插入次数

给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符。

请你返回让 s 成为回文串的 最少操作次数 。

「回文串」是正读和反读都相同的字符串

1.状态表示:用dp[ i ][ j ]表示以 i 开始,j 结尾的最少插入次数

2.状态转移方程:

                if (s[ i ] == s[ j ]) dp[ i ][ j ] = dp[ i + 1 ][ j - 1 ];

                else dp[ i ][ j ] = min(dp[ i ][ j - 1 ], dp [ i + 1 ][ j ]) + 1;

3.初始化:无需初始化

4.填表顺序:每一行从上往下,每一列从左往右

5.返回值:dp[ 0 ][ n - 1 ]

class Solution {
public:
    int minInsertions(string s) 
    {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        for(int i = n - 1; i >= 0; --i)
            for(int j = i + 1; j < n; ++j)
            {
                if(s[i] == s[j]) dp[i][j] = dp[i + 1][j - 1];
                else dp[i][j] = min(dp[i][j - 1], dp[i + 1][j]) + 1;
            }

        return dp[0][n - 1];
    }
};

这是ac代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值