• 回文串问题


    目录

    回文子串

    解法一【中心扩散法】

    解法二【动态规划法】

    最长回文子串

    分割回文串IV

    分割回文串II

    最长回文子序列

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


    声明:下面的讲解,主要采用动态规划法来解决!!! 

    回文子串

    题目

    思路

    判断一个字符串是否是回文的,我们很容易想到中心扩散法,从字符串中间向两端扩散或者两头向中间收拢,还可以采用动态规划来解决,下面将介绍两种方法来解决这道题。

    解法一【中心扩散法】

    通过穷举所有可能的情况,并判断该字符串是否是回文串

    代码

    1. class Solution {
    2. public:
    3. int sum=0;
    4. int countSubstrings(string s) {
    5. helper(s,0);
    6. return sum;
    7. }
    8. void helper(string s,int start){
    9. if(start==s.size()) return;
    10. for(int i=start;isize();i++){
    11. if(isPalindrome(s,start,i)){
    12. sum++;
    13. }
    14. }
    15. helper(s,start+1);
    16. }
    17. bool isPalindrome(string s,int l,int r){
    18. while(l
    19. if(s[l++]!=s[r--])
    20. return false;
    21. }
    22. return true;
    23. }
    24. };
    解法二【动态规划法

    状态表示:dp[i][j]表示字符串[i,j]区间的子字符串是否是回文串。

    状态转移方程:

    初始化:根据状态转移方程推知,由于我们已经加了限制条件,所以不用初始化。

    填表顺序:从下往上。

    我们只需要填写红色区域的值就可以了,最终会得到字符串所有子字符串是否是回文串的结果,这样只需要遍历图中红色区域,就可以知道这个字符串中有多少个回文子字符串了。

    代码

    1. class Solution {
    2. public:
    3. int sum=0;
    4. int countSubstrings(string s) {
    5. int n=s.size();
    6. vectorbool>> dp(n,vector<bool>(n));
    7. //dp[i][j]=dp[i+1][j-1]
    8. for(int i=n-1;i>=0;i--)
    9. for(int j=n-1;j>=i;j--){
    10. if(s[i]!=s[j]) dp[i][j]=false;
    11. else{
    12. if(i==j) dp[i][j]=true;
    13. else if(i+1==j) dp[i][j]=true;
    14. else
    15. dp[i][j]=dp[i+1][j-1];
    16. }
    17. }
    18. for(int i=0;i
    19. for(int j=i;j
    20. if(dp[i][j]) sum++;
    21. return sum;
    22. }
    23. };
    最长回文子串

    题目

    思路

    和上一道题一样,我们需要穷举出该字符串所有子串的情况,并判断是否是回文串,而且需要记录是回文串的起始位置和长度,便于找出最长回文子串,如果采用中心扩散法,时间复杂度是O(N^3),可能会超时,但经过我尝试,这种方法的确会超时。所以下面我们采用动态规划,穷举出该字符串所有子串的情况,并判断是否是回文串,时间复杂度是O(N^2),不会超时。

    解法

    状态表示:dp[i][j]表示字符串[i,j]区间的子字符串是否是回文串。

    状态转移方程:

    初始化:根据状态转移方程推知,由于我们已经加了限制条件,所以不用初始化。

    填表顺序:从下往上。

    我们只需要填写红色区域的值就可以了,最终会得到字符串所有子字符串是否是回文串的结果,这样只需要遍历图中红色区域,就可以知道这个字符串中有多少个回文子字符串了。

    代码

    1. class Solution {
    2. public:
    3. string longestPalindrome(string s) {
    4. int n=s.size();
    5. vectorbool>> dp(n,vector<bool>(n));
    6. //dp[i][j]=dp[i+1][j-1]
    7. for(int i=n-1;i>=0;i--)
    8. for(int j=n-1;j>=i;j--){
    9. if(s[i]!=s[j]) dp[i][j]=false;
    10. else{
    11. if(i==j) dp[i][j]=true;
    12. else if(i+1==j) dp[i][j]=true;
    13. else
    14. dp[i][j]=dp[i+1][j-1];
    15. }
    16. }
    17. int len=1,pos=0;
    18. for(int i=0;i
    19. for(int j=i;j
    20. if(dp[i][j] && j-i+1>len){
    21. len=j-i+1;
    22. pos=i;
    23. }
    24. return s.substr(pos,len);
    25. }
    26. };
    分割回文串IV

    题目

    思路

    其实解法就是我们找到两个位置,将字符串分割为三段区间,并且满足三段区间都是回文串即可,如果采用中心扩散法,分析可知时间复杂度是O(N^3),可能会超时,所以接下来,我们依旧采用一种算是“一劳永逸”的解法,动态规划,我们会觉得这道题不是困难题了,而是so easy。

    解法

    状态表示:dp[i][j]表示字符串[i,j]区间的子字符串是否是回文串。

    状态转移方程:

    初始化:根据状态转移方程推知,由于我们已经加了限制条件,所以不用初始化。

    填表顺序:从下往上。

    我们只需要填写红色区域的值就可以了,最终会得到字符串所有子字符串是否是回文串的结果,这样只需要遍历图中红色区域,就可以知道这个字符串中有多少个回文子字符串了。

    代码

    1. class Solution {
    2. public:
    3. bool checkPartitioning(string s) {
    4. int n=s.size();
    5. vectorbool>> dp(n,vector<bool>(n));
    6. //dp[i][j]=dp[i+1][j-1]
    7. for(int i=n-1;i>=0;i--)
    8. for(int j=i;j
    9. if(s[i]==s[j])
    10. dp[i][j]=i+11][j-1]:true;
    11. }
    12. //[0,i-1] [i,j] [j+1,n-1]
    13. for(int i=1;i-1;i++)
    14. for(int j=i;j-1;j++)
    15. if(dp[0][i-1] && dp[i][j] && dp[j+1][n-1]) return true;
    16. return false;
    17. }
    18. };
    分割回文串II

    题目

    思路

    我们可以尝试以某个位置为结尾,并判断[0,i]区间是否是回文串,如果是的话,这段区间的最少分割次数就是0,如果不是的话,需要分情况讨论。

    先声明一点,下面我依旧先使用动态规划穷举出所有可能的子字符串,下一步还会使用动态规划,这样的话,就会两使用dp来表示,肯定是不可以的,但是基于前一步的动态规划在前几道题已经讲过,所以这里就不再重新画图了,把前面的图拿过来直接使用了!!!!

    状态表示:dp[i][j]表示字符串[i,j]区间的子字符串是否是回文串。

    状态转移方程:

    初始化:根据状态转移方程推知,由于我们已经加了限制条件,所以不用初始化。

    填表顺序:从下往上。

    我们只需要填写红色区域的值就可以了,最终会得到字符串所有子字符串是否是回文串的结果,这样只需要遍历图中红色区域,就可以知道这个字符串中有多少个回文子字符串了。

    填表顺序:从下往上。

    状态表示:dp[i]表示以i位置为结尾,使[0,i]区间的字符串成为回文子串的最少分割次数。

    填表顺序:从左往右。

    代码

    1. class Solution {
    2. public:
    3. int minCut(string s) {
    4. int n=s.size();
    5. vectorbool>> isP(n,vector<bool>(n));
    6. //dp[i][j]=dp[i+1][j-1]
    7. for(int i=n-1;i>=0;i--)
    8. for(int j=i;j
    9. if(s[i]==s[j])
    10. isP[i][j]=i+11][j-1]:true;
    11. }
    12. vector<int> dp(n,0x3f3f3f3f);
    13. //dp[i] 表示从0到i最少分割次数
    14. for(int i=0;i
    15. {
    16. if(isP[0][i]) dp[i]=0;
    17. else{
    18. for(int j=i;j>0;j--)
    19. if(isP[j][i]) dp[i]=min(dp[j-1]+1,dp[i]);
    20. }
    21. }
    22. return dp[n-1];
    23. }
    24. };
    最长回文子序列

    题目

    思路

    如果我们依旧采用以某个位置为结尾来分析问题,会发现推不出状态转移方程,下面尝试采用以某一段区间来进行分析本题。

    状态表示:dp[i,j]表示[i,j]区间内最长的回文子序列长度。

    状态转移方程:

    填表顺序:从下往上,从左往右。

    代码

    1. class Solution {
    2. public:
    3. int longestPalindromeSubseq(string s) {
    4. int n=s.size();
    5. vectorint>> dp(n,vector<int>(n));
    6. //dp[i][j] 表示[i,j]区间最长回文子序列长度
    7. for(int i=n-1;i>=0;i--)
    8. {
    9. dp[i][i]=1;
    10. for(int j=i+1;j
    11. if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2;
    12. else dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
    13. }
    14. }
    15. return dp[0][n-1];
    16. }
    17. };
    让字符串成为回文串的最少插入次数

    题目

    思路

    如果我们依旧采用以某个位置为结尾来分析问题,会发现推不出状态转移方程,下面我们尝试采用以某一段区间来进行分析本题。

    状态表示:dp[i,j]表示让[i,j]区间字符串成为回文串的最少插入次数。

    状态转移方程:

    填表顺序:从下往上,从左往右。

    代码

    1. class Solution {
    2. public:
    3. int minInsertions(string s) {
    4. int n=s.size();
    5. vectorint>> dp(n,vector<int>(n));
    6. //dp[i][j]表示[i,j]区间称为回文串的最少插入次数
    7. for(int i=n-1;i>=0;i--){
    8. dp[i][i]=0;
    9. for(int j=i+1;j
    10. if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
    11. else dp[i][j]=min(dp[i][j-1],dp[i+1][j])+1;
    12. }
    13. }
    14. return dp[0][n-1];
    15. }
    16. };

  • 相关阅读:
    手机 IOS 软件 IPA 签名下载安装详情图文教程
    Docker学习
    双11,客服系统让你告别客服节前emo
    confluence
    @Transactional 注解 同一个类下的两个方法
    什么是Generator函数?如何使用yield表达式他是干嘛的?next()方法是做什么的?
    关于mac终端 无法显示中文 + echo cut 组合问题
    Linux之GPIO应用
    万字长文!对比分析了多款存储方案,KeeWiDB最终选择自己来
    电脑版微信文件存储在哪个文件夹可以找到
  • 原文地址:https://blog.csdn.net/wmh_1234567/article/details/140001397