回文子串、回文子序列相关题目

回文子串、回文子序列相关题目

回文子串是要连续的,回文子序列可不是连续的。

516. 最长回文子序列

dp数组含义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示子序列 s [ i , j ] s[i,j] s[i,j] 中的最长回文子序列的长度。

dp数组初始化:子序列长度为 1 时,最长回文子序列的长度就是 1, 即 s [ i , i ] = 1 s[i,i]=1 s[i,i]=1

递推公式

  • 如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j] ,那么相比于 s [ i + 1 , j − 1 ] s[i+1,j-1] s[i+1,j1] s [ i , j ] s[i,j] s[i,j] 的最长子序列的长度增加了 2(首尾)
  • 如果 s [ i ] ≠ s [ j ] s[i]\ne s[j] s[i]=s[j],那么 s [ i , j ] s[i,j] s[i,j] 的最长子序列的长度就是 s [ i + 1 , j ] , s [ i , j − 1 ] s[i+1,j],s[i,j-1] s[i+1,j],s[i,j1] 中的较大者(取首或者取尾)

遍历顺序

这里顺序比较讲究,我们知道,动态规划解法的遍历顺序需要遵循的原则要按照递推公式的依赖关系,即递推公式中计算 dp 数组中的某个值时一定要保证它所依赖的值已经在 dp 数组中被计算好了

在本题中,我们看到递推公式中, d p [ i ] [ j ] dp[i][j] dp[i][j] 的值依赖于三个值: d p [ i + 1 ] [ j − 1 ] dp[i+1][j-1] dp[i+1][j1] d p [ i + 1 ] [ j ] dp[i+1][j] dp[i+1][j] d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1],我们所选择的遍历顺序需要保证在计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 时这三个值都已经计算过了,那么很明显的, i i i 要从大往小, j j j 要从小往大。

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int> (n));
        for (int i=0; i<n; ++i) dp[i][i] = 1;
        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] + 2;
                else dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
            }
        }
        return dp[0][n-1];
    }
};
5. 最长回文子串

dp数组含义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示子串 s [ i , j ] s[i, j] s[i,j] 是否为回文串。

dp数组初始化:长度为 1 的子串,即 s [ i , i ] s[i,i] s[i,i] 一定是回文串。

递推公式

  • 如果 s [ i ] ≠ s [ j ] s[i]\ne s[j] s[i]=s[j],那么 s [ i , j ] s[i,j] s[i,j] 一定不是回文串;
  • 如果 s [ i ] = = s [ j ] s[i]==s[j] s[i]==s[j], 那要再看子串的长度:
    • 如果子串长度小于等于 3, 那 s [ i , j ] s[i,j] s[i,j] 一定是回文串
    • 如果子串长度大于 3,则 s [ i , j ] s[i,j] s[i,j] 是不是回文串就取决于 s [ i + 1 ] [ j − 1 ] s[i+1][j-1] s[i+1][j1] 是不是回文串。如 sabas 是不是回文串取决于 aba 是不是回文串。

遍历顺序

外层循环遍历子串的长度,内层循环遍历起始位置,这里也可以考虑与上面类似的遍历顺序思路,会在下一题中给出代码。

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        if (n < 2) return s;
        string ans(1, s[0]);
        vector<vector<bool>> dp(n, vector<bool> (n));
        for (int i=0; i<n; ++i) dp[i][i] = true;
        for (int len=2; len<=n; ++len) {
            for (int i=0; i<n; ++i) {
                int j = i + len - 1;
                if (j >= n) break;
                if (s[i] != s[j]) dp[i][j] = false;
                else {
                    if (len <= 3) dp[i][j] = true;
                    else dp[i][j] = dp[i+1][j-1];
                }
                if (dp[i][j] && len>ans.size()) ans = s.substr(i, len);
            }
        }
        return ans;
    }
};
647. 回文子串

思路和上一题逻辑类似

遍历顺序一,与上题一致

class Solution {
public:
    int countSubstrings(string s) {
        int n = s.size();
        if (n < 2) return n;
        vector<vector<bool>> dp(n, vector<bool> (n));
        for (int i=0; i<n; ++i) dp[i][i] = true;
        int ans = n;
        for (int len=2; len<=n; ++len) {
            for (int i=0; i<n; ++i) {
                int j = i + len - 1;
                if (j >= n) break;
                if (s[i] != s[j]) dp[i][j] = false;
                else {
                    if (len <= 3) {
                        dp[i][j] = true;
                        ++ans;
                    }
                    else {
                        if (dp[i+1][j-1]) {
                            dp[i][j] = true;
                            ++ans;
                        }
                        else dp[i][j] = false;
                    }
                }
            }
        }
        return ans;
    }
};

另一种根据长度和起始位置的遍历顺序,思路类似题516:

class Solution {
public:
    int countSubstrings(string s) {
        int n = s.size();
        int ans = 0;
        vector<vector<bool>> dp(n, vector<bool> (n, false));
        for (int i=n-1; i>=0; --i) {
            for (int j=i; j<n; ++j) {
                if (s[i] == s[j]) {
                    if (abs(i-j) <= 1) {
                        dp[i][j] = true;
                        ++ans;
                    }
                    else {
                        if (dp[i+1][j-1]) {
                            dp[i][j] = true;
                            ++ans;
                        }
                    }
                }
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值