动规:回文串问题

目录

[No.1 回文子串](#No.1 回文子串)

[No.2 最长回文子串](#No.2 最长回文子串)

[No.3 分隔回文串IV](#No.3 分隔回文串IV)

[No.4 分割回文串II](#No.4 分割回文串II)

[No.5 最长回文子序列](#No.5 最长回文子序列)

[No.6 让字符串成为回文串的最少插入次数](#No.6 让字符串成为回文串的最少插入次数)


No.1 回文子串

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

题目描述:

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

子串也要求连续性。

返回回文子串数量。

思路:

ps:可以用更优的 中心扩展算法 or 马拉车算法 来解决这道题。这里只介绍动规。

我们可以通过dp,以n方级别复杂度将所有的子串是否回文的信息,保存在dp表里面。

创建二维dp表,i表示起始位置,j从i开始往后遍历。

si==sj后的细分判断顺序很好的避免了越界

回文串的填表顺序有点不同,二维表的填表顺序应该是从下往上因为dpij需要考虑dpi+1j-1

最后返回dp表里true的个数。完美解决。

cpp 复制代码
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;
    }
};

No.2 最长回文子串

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

思路和上一题一致,填表过程中更新最长回文子串的起始位置和大小

cpp 复制代码
class Solution {
public:
    string longestPalindrome(string s) {
        int n=s.size(),len=0,begin=0;
        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;
                if(dp[i][j]&&len<j-i+1)
                {
                    len=j-i+1;begin=i;
                }
            }
        }
        return s.substr(begin,len);
    }
};

No.3 分隔回文串IV

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

思路:

暴力:将字符串分成三个部分的所有情况都枚举,判断有没有全都是回文的。

转成dp也很好想

cpp 复制代码
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-1;++i)
        {
            for(int j=i;j<n-1;++j)
            {
                if(dp[0][i-1]&&dp[i][j]&&dp[j+1][n-1])return true;
            }
        }
        return false;
    }
};

No.4 分割回文串II

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

思路:

既然已经知道我们可以通过dp表将任何区间的回文情况存好,那么这题就相当于在问,将原数组分成区间,让区间都是true的同时数量最少。无非就是判断区间内能不能符合子串,有点类似单词划分那题。

因为要求min,初始化所有值为INT_MAX

cpp 复制代码
class Solution {
public:
    int minCut(string s) {
        int n=s.size();
        vector<vector<bool>> isPal(n,vector<bool>(n));
        for(int i=n-1;i>=0;--i)
        {
            for(int j=i;j<n;++j)
            {
                if(s[i]==s[j])isPal[i][j]=i+1<j?isPal[i+1][j-1]:true;
            }
        }
        vector<int> dp(n,INT_MAX);
        for(int i=0;i<n;++i)
        {
            if(isPal[0][i])dp[i]=0;
            else
            {
                for(int j=1;j<=i;++j)
                {
                    if(isPal[j][i])dp[i]=min(dp[i],dp[j-1]+1);
                }
            }
        }
        return dp[n-1];
    }
};

No.5 最长回文子序列

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

子序列,和前面的子串不一样了。

回文:二维表存储?i~j存储回文信息?和之前不一样,子序列不要求连续。那么这里我们选择dpij表示i~j内部最长子序列长度。回文判断:每次往两边扩展。

子序列:二维表ij表示i~j区间内的最长回文子序列长度

填表顺序从下到上,从左到右

初始化将主对角上的值设置为1,因为dp值表示区间内最大子序列长度

cpp 复制代码
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)
        {
            //i==j
            dp[i][i]=1;
            //从i+1开始遍历,融合相等的另外两种分支
            for(int j=i+1;j<n;++j)
            {
                if(s[i]==s[j])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];
    }
};

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

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

需要二维表分区间讨论

不等时,分别向左右讨论

cpp 复制代码
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]=1+min(dp[i][j-1],dp[i+1][j]);
            }
        }
        return dp[0][n-1];
    }
};

回文串的问题可以转换成区间讨论的问题,这有利于我们确定最重要的状态表示。( •̀ ω •́ )✧

此篇完。

相关推荐
不爱学英文的码字机器16 分钟前
推荐算法梳理,六种主流模型与九步训练流程
算法·机器学习·推荐算法
白狐_7981 小时前
408数据结构第5章:树与二叉树②——遍历、线索树、森林与哈夫曼
数据结构·算法·深度优先
302wanger2 小时前
【阅读笔记】当AI拿走一切之后
笔记
疯狂打码的少年3 小时前
【数据结构】串(字符串):基本概念与存储
数据结构·笔记
for_ever_love__3 小时前
python基础语法学习: 数据容器
windows·python·学习
云空4 小时前
《电子专业完整学习路线图(分两套:本科四年版 / 零基础自学速成版)》
科技·学习·学习方法·高考
min(a,b)4 小时前
学习第16天:LangGraph 与 Agent 工作流编排
学习
woshihuanglaoshi4 小时前
鸿蒙技术进阶全景高级成长体系:从初中级到架构师的技能树/学习路径/项目实战/认证体系系统性方法论
学习·华为·harmonyos
AI即插即用4 小时前
即插即用系列 | IEEE TMI PLG-HN:原型学习引导的 CNN-Transformer 混合网络,攻克乳腺肿瘤分割难题
人工智能·深度学习·神经网络·学习·目标检测·cnn·transformer
致Great4 小时前
科研人的 AI,不该只回答问题:我用字节TraeWork 跑了一遍真实研究任务
算法