回文串问题

本文介绍了五种与回文字符串相关的问题,包括查找回文子串的数量、最长回文子串、分割回文串以及使其成为回文串的最少插入次数,均通过动态规划的方法进行求解。
摘要由CSDN通过智能技术生成

1、回文子串

647. 回文子串 - 力扣(LeetCode)

class Solution 
{
    //1、状态表示:
    // dp[i][j]:s字符串种从i位置到j位置的子串,是否是回文串

    //2、状态转移方程:
    //if s[i] != s[j],false
    //如果s[i] == s[j]:(1)i == j,true;(2) i+1 == j相邻,true;(3)如果不是前面两种关系,那么就去看dp[i+1][j-1]

    //3、初始化:其实不用初始化,因为dp[i+1][j-1]不会越界

    //4、填表顺序:dp[i][j]需要dp[i+1][j-1],左下角。所以需要从下往上

    //5、找一找dp表中true的个数
public:
    int countSubstrings(string s) 
    {
        int len = s.size();
        int res = 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] == true) res++;
            }
        return res;
    }
};

2、最长回文子串

5. 最长回文子串 - 力扣(LeetCode)

class Solution 
{
    //dp[i][j]:存储从[i,j]位置的子串是不是回文串
public:
    string longestPalindrome(string s) 
    {
        int len = s.size();
        vector<vector<bool>> dp(len,vector<bool>(len));
        string res = "";
        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] == true)
                {
                    string newstr(s.begin() + i,s.begin()+j+1);//判断长度
                    if(res.size() < newstr.size())
                    {
                        res = newstr;
                    }
                }
            }
        return res;
    }
};

3、分割回文串Ⅳ

1745. 分割回文串 IV - 力扣(LeetCode)

class Solution 
{
    //dp[i][j]:存储从[i,j]位置的子串是不是回文串
public:
    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;
            }
        //判断dp[0][j] && dp[j+1][k] && dp[k+1][len-1]
        for(int j = 0;j<len-1;j++)
            for(int k = j + 1;k <len-1;k++)
            {
                if(dp[0][j] && dp[j+1][k] && dp[k+1][len-1]) return true;
            }
        return false;
    }
};

4、分割回文串Ⅱ

132. 分割回文串 II - 力扣(LeetCode)

class Solution 
{
    //1、状态表示:
    // 经验 + 题目要求:
    // dp[i]表示:s[0,i]区间上的字符串,最少分割次数

    //2、状态转移方程
    //(1)[0,i]本身就是回文,-->0
    //(2)[0,i]本身不是回文,在[0,i]中取一个j(分割),0 < j <= i,判断从[j,i]是否是回文串
    //  如果是,那么[j,i]是0,总的dp[i] = dp[j-1] + 1,这个1代表将字符串切成[0,j-1]和[j,i]
    // 则dp[i] = min(dp[i-1] + 1,dp[i]);
    //  如果不是,那么就不用考虑,j继续寻找
    // 所以用一个judge[x][y]来存储[x,y]这个子串是不是回文

    //3、初始化:dp[i] = min(dp[i-1] + 1,dp[i]);由于这里取min,并且j-1不会越界,所以初始化为无穷大
    //防止干扰

    //4、dp[i]需要dp[j-1],所以从左往右

    //5、return dp[n-1];
public:
    int minCut(string s) 
    {
        int len = s.size();
        vector<vector<bool>> judge(len,vector<bool>(len));
        for(int i = len-1;i>=0;i--)
            for(int j = i;j<len;j++)
            {
                if(s[i] == s[j])
                    judge[i][j] = i + 1 < j ? judge[i+1][j-1] : true;
            }
        
        vector<int> dp(len,0x3f3f3f3f);
        for(int i = 0;i<len;i++)
        {
            if(judge[0][i] == true) dp[i] = 0;
            else
            {
                for(int j = 1;j<=i;j++)
                    if(judge[j][i]) dp[i] = min(dp[j-1] + 1,dp[i]);
            }
        }
        return dp[len-1];
    }
};

5、最长回文子序列

516. 最长回文子序列 - 力扣(LeetCode)

class Solution 
{
    //1、dp[i][j]:s字符串里[i,j]区间内的所有的子序列,最长的回文子序列的长度
    //2、状态转移方程:
    //(1)s[i] == [j]:如果i == j,dp[i][j] = 1;如果i + 1 == j,相邻,,dp[i][j] = 2;
    //如果i,j之间有其余字符,那么就是dp[i][j] = dp[i+1][j-1] + 2,+2代表加上i和j
    //(2)s[i] != s[j]:那么一定不可能以i开头j结尾,那么区间可以变为[i+1,j]或者[i,j-1]
    //所以dp[i][j] = max(dp[i+1][j],dp[i][j-1])

    //3、初始化:dp[i][j]需要用到dp[i+1][j]和dp[i][j-1],下面和左边,并且j-1不会越界
    //所以不用初始化

    //顺序:
    //从下往上填写,每一行从左往右

    //return dp[0][n-1]
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;//这就是i == j
            for(int j = i + 1;j<n;j++)
            {
                if(s[i] == s[j]) dp[i][j] = dp[i+1][j-1] + 2;//这里包含了dp[i][j] = 2和dp[i][j] = dp[i+1][j-1] + 2
                else dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
            }
        }
        return dp[0][n-1];
    }
};

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

1312. 让字符串成为回文串的最少插入次数 - 力扣(LeetCode)

class Solution 
{
    //dp[i][j]:s里面[i,j]区间内的子串,使它成为回文串的最小插入次数

    //状态转移方程:
    //(1)如果s[i] == s[j],j >= i:
    //  如果i == j,则dp[i][j] = 0;
    //  如果i + 1 == j相邻,则dp[i][j] = 0;
    //  如果i + 1 < j,即i,j中间还有其它字符,则dp[i][j] = dp[i+1][j-1]
    //(2)如果s[i] != s[j],
    //  可以在[i,j]左边加一个s[j],然后让[i,j-1]成为回文串,dp[i][j] = dp[i][j-1] + 1;
    //  也可以在[i,j]右边加一个s[i],然后让[i+1,j]成为回文串,dp[i][j] = dp[i+1][j] + 1;
    //因此:dp[i][j] = min(dp[i][j-1] + 1,dp[i+1][j] + 1)

    //初始化:
    //dp[i][j]需要dp[i+1][j-1],左下角
    //  需要dp[i][j-1]和dp[i+1][j],左边和下边,是不会越界的
    //所以不用初始化

    //顺序:从下往上,从左往右

    //return dp[0][n-1]
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];//这里是i+1<=j
                else dp[i][j] = min(dp[i][j-1],dp[i+1][j]) + 1;
        return dp[0][n-1];
    }
};

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夹心宝贝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值