[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题


◆ 博主名称: 小此方-CSDN博客 大家好,欢迎来到小此方的博客。
⭐️算法系列个人专栏: 算法实战论
⭐️此方的GitHub: github_此方
⭐️ Re系列专栏:我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)


文章目录


概要&序論

  回文串问题一直都很头疼,但是当你学完动态规划的回文串解题思路。我可以保证------90%的回文串问题都会从中等困难题变成简单题。

一、聊聊回文串问题的一般解法有哪几种

中心扩展算法 -> n² / 1

马拉车算法 -> n / n

动态规划 -> n² / n²

  中心扩展算法,大概是大家刚开始解除算法的时候采用的一种方法,简单,但是面对复杂问题需要好好动动脑子。马拉车算法,效率高,但是不好学,并且限制比较多。

  动态规划算法,虽然时间复杂度和空间复杂度不怎么样,但是他最简单,而且非常的万能。他的根本思想是将所有的字串是否回文的信息,存放在dp表里面。

  我们的讲法是:一道母题,全盘照搬------就是这么简单粗暴。

二、动态规划解决回文串问题的母题

回文子串

  状态表示:对于回文串问题,我们之前讲过的"以i位置为结尾"的经验就不可以使用了------你需要知道一个新的经验。

dpij 表示 s 字符串 i, j 的子串,是否是回文串true/false

  状态转移方程:

  填表顺序:,大方向上来看,是自下而上。

  需要初始化吗?显然不需要

  代码:

cpp 复制代码
        vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
        for(int i = s.size()-1 ; i >= 0 ; i --){
            for(int j = i ; j < s.size() ; j ++){
                if(s[i] == s[j]){
                    if(i == j) dp[i][j] = true ;
                    else if(i +1 == j) dp[i][j] = true;
                    else dp[i][j] = dp[i+1][j-1];
                }
            }
        }

  统计回文串个数

cpp 复制代码
        int count = 0 ;
        for(auto e : dp)
            for(auto t : e)
                if(t == true)
                    count ++;
        return count;

三、子题就是在母题的基础上稍微变形

  最长回文子串这题是将计数改为实时统计最长子序列与最长子序列的长度

cpp 复制代码
        vector<vector<int>> dp(s.size(),vector<int>(s.size(),0));
        int MaxLen = 0 ;
        std::string MaxString = "";
        for(int i = s.size()-1 ; i >= 0 ; i --){//i是回文字串的左端点
            for(int j = i ; j < s.size() ; j ++){
                //j是回文字串的右端点,j必须大于等于i
                if(s[i] == s[j]){
                    if(i == j) dp[i][j] = 1;
                    else if(i+1 == j) dp[i][j] = 2;
                    else{
                        if(dp[i+1][j-1] == 0){
                            dp[i][j] = 0;
                        }else{
                            dp[i][j] = dp[i+1][j-1] + 2;
                        }
                    }
                }
                if(MaxLen < dp[i][j]){
                    MaxLen = dp[i][j];
                    MaxString = s.substr(i,j-i+1);
                }
            }
        }
        return MaxString;

  回文串分割 IV这题是在原基础上做一个遍历分割

cpp 复制代码
        if(s.size() <= 2){
            return false;
        }
        vector<vector<bool>>dp(s.size(),vector<bool>(s.size(),false));
        for(int i = s.size()-1 ; i >= 0 ; i --){
            for(int j = i ; j < s.size() ; j ++){
                if(s[i] == s[j]){
                    if(i == j || i+1 == j){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
            }
        }
        for(int i = 1 ; i < s.size()-1 ; i ++){
            for(int j = i ; j < s.size()-1 ; j ++){
                //0~i, i~j , j~s.size()-1
                //i和j不能和边界重合,因为如果发生,那么就会少一段
                //i和j可以重合,应为重合后也算一段
                if(dp[0][i-1] && dp[i][j] && dp[j+1][s.size()-1]){
                    return true;
                }
            }
        }
        return false;

分割回文串 II

cpp 复制代码
        vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
        for(int i = s.size()-1 ; i >= 0 ; i --){
            for(int j = i ; j < s.size() ; j ++){
                if(s[i] == s[j]){
                    if(i == j || i+1 == j){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i+1][j-1];
                    }
                }
            }
        }
        //本体真正要处理的动态规划:
        vector<int> Sdp(s.size(),INT_MAX);//表示以i
        //为结尾[0,i]的字串的最少回文字串分割次数
        for(int i = 0 ; i < s.size() ; i ++){
            if(dp[0][i] == true){
                Sdp[i] = 0;
                continue;
            }
            int MinLen = INT_MAX;
            for(int j = 0 ; j <= i ; j ++){
                //[0 ,j-1],[j,i],我要让后者是一个回文串,然后前者进一步分割
                if(dp[j][i] == true && j-1 >= 0){
                    MinLen = min(MinLen, Sdp[j-1] + 1);
                }
            }
            Sdp[i] = MinLen;
        }
        return Sdp[s.size()-1];

四、有时候numsi != numsj的情况也要考量

最长回文子序列

让字符串成为回文串的最小插入次数

  讲一个回文子序列,dpij表示以ij为左右端点的最长回文子序列的长度,如果si == sj 那么我们可以继续考量dpi+1j-1。否则:

  去掉左边那个或者右边那个。然后再判断左右是否相等,并进一步判断dpi+1j-1。为什么这么想?因为dpij-1,dpi+1j,已经包含了所有的情况。

cpp 复制代码
        vector<vector<int>> dp(s.size(),vector<int>(s.size(),1));
        int MaxLen = 1 ;
        for(int i = s.size()-1 ; i >= 0 ; i --){
            for(int j = i ; j <= s.size()-1 ; j ++){
                if(s[i] == s[j]){
                    if(i == j) dp[i][j] = 1 ;
                    else if(i+1 == j) dp[i][j] = 2 ;
                    else dp[i][j] = dp[i+1][j-1] + 2 ;
                }else{
                    dp[i][j] = max(dp[i][j-1],dp[i+1][j]);
                }
                MaxLen = max(MaxLen,dp[i][j]);
            }
        }
        return MaxLen;

好的本文内容到此结束,我是此方,我们下期再见。

相关推荐
Pniubi1 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH1 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi2 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
東隅已逝,桑榆非晚4 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom4 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
bro_Java6665 小时前
《排序的底层逻辑:比较、交换与分治》
java·算法·排序算法
爱喝雪碧的可乐5 小时前
MATLAB
开发语言·算法·matlab
叼烟扛炮6 小时前
Linux 学习笔记(六十):单线程的逆袭——epoll 高并发服务器实战
linux·服务器·网络·笔记·学习·算法
careathers7 小时前
【数据结构】基于比较的排序算法 - 交换排序
数据结构·算法·排序算法