LeetCode几道回文串题目

647. Palindromic Substrings

给你一个长度不超过1000的字符串,判断该字符串包含多少回文子串。


暴力法: 枚举子串,判断是否回文
动态规划:定义状态dp[i, j]为子串[i, j]是否为回文串。(这个状态定义在许多回文串问题中都有用到)
递归方程为

dp[i, j] = j == i+1 ? s[i] == s[j] : dp[i+1, j-1] && s[i] == s[j]

子串[i, j]为回文串的要求是s[i] == s[j] 并且 dp[i+1, j-1]也是回文子串。

class Solution {
public:
    int countSubstrings(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for (int i = 0; i < n; i++)
            dp[i][i] = 1;
        int res = n;
        for (int l = 2; l <= n; l++) {
            for (int i = 0; i+l-1 < n; i++) {
                int j = i+l-1;
                if (j == i+1) dp[i][j] = s[i] == s[j];
                else dp[i][j] = dp[i+1][j-1] && s[i] == s[j];
                res += dp[i][j];
            }
        }
        return res;

    }
};

516. Longest Palindromic Subsequence

求给定字符串的最长回文子序列


定义dp[i, j]代表字符串s[i, j]内的最长回文子序列的长度。
递推公式:

if s[i] == s[j]:
    dp[i, j] = j == i+1 ? 2 : dp[i+1, j-1] + 2;
else:
    dp[i, j] = max(dp[i+1, j], dp[i, j-1]);

很像LCS问题的递推公式

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int len = s.size();
        vector<vector<int>> isPalindrome(len, vector<int>(len, false));
        for (int i = 0; i < len; i++) {
            isPalindrome[i][i] = 1;
        }
        for (int l = 2; l <= len; l++) {
            for (int i = 0; i < len+1-l; i++) {
                int j = i+l-1;
                if (s[i] == s[j]) {
                    isPalindrome[i][j] = l == 2 ? 2 : isPalindrome[i+1][j-1]+2;
                } else {
                    isPalindrome[i][j] = max(isPalindrome[i+1][j], isPalindrome[i][j-1]);
                }
            }
        }
        return isPalindrome[0][len-1];

    }
};

5. Longest Palindromic Substring

求给定字符串的最长回文子串


如果仍然用之前的dp方法,先求出dp[i, j],然后再从最长的子串开始找,复杂度为O(n2),在leetcode里可以ac,但是当字符串长度为1e6级别时显然就超时了(hdu 3068)。这时候就有马拉车算法,一个O(n)时间解决该问题的算法。
Manacher算法参考链接:
1. Manacher算法
2. hdu3068之manacher算法+详解
3. Manacher
4. manacher(马拉车)算法详解+例题一道【bzoj3790】【神奇项链
5. 我相信聪明的你经过以上博客一定对该算法有了大概的了解,总结来说,记住几个辅助数组与变量

mx - 所有回文串中所能延伸到最右端的位置
id - 上述mx的所对应回文串的中心点
p[i] - 以i开始,到以i为中心的回文串的最右端的长度
经过预处理(添加'#')让所有的回文串都变成了奇数
class Solution {
public:
    string longestPalindrome(string s) {
        string str = "$"; // 开头添加一个字符是为了在while循环中不用担心越界问题
        for (auto c : s) {
            str.push_back('#');
            str.push_back(c);
        }
        str.push_back('#');
        vector<int> p(str.size(), 0);
        int mx = 0, id = 0, ans = 0, left = 0;
        for (int i = 1; i < str.size(); i++) {
            if (i < mx)
                p[i] = min(p[2*id-i], mx-i);
            else
                p[i] = 1;
            while (str[i-p[i]] == str[i+p[i]]) p[i]++;
            if (mx < i+p[i]) {
                mx = i+p[i];
                id = i;
            }
            if (p[i] > ans) {
                ans = p[i];
                left = i-p[i]+1;
            }
        }
        if (left == '#') left++;
        left /= 2;
        return s.substr(left, ans-1);
    }
};

730. Count Different Palindromic Subsequences

1.LeetCode 730. Count Different Palindromic Subsequences
题解很清晰,有图。这道题需要过段时间二刷。

class Solution {
public:
int countPalindromicSubsequences(string s) {
    long long mod = 1000000007;
    int n = s.size();
    vector<vector<long long>> dp(n, vector<long long>(n, 0));
    //dp[i][j]代表[i, j]内回文子序列的个数
    for (int i = 0; i < n; i++) dp[i][i] = 1;
    for (int l = 2; l <= n; l++)
        for (int i= 0; i+l-1 < n; i++) {
            int j = i+l-1;
            if (s[i] != s[j])
                dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1];
            else {
                dp[i][j] = dp[i+1][j-1] * 2;
                int left = i+1, right = j-1;
                while (left <= right && s[left] != s[i]) left++;
                while (right >= left && s[right] != s[i]) right--;
                if (left > right) {
                    //there is no s[i] in s[i+1:j-1]
                    dp[i][j] += 2;
                } else if (left == right) {
                    //there is one s[i] in s[i+1:j-1]
                    dp[i][j] += 1;
                } else if (left < right) {
                    //there is more than one s[i] in s[i+1:j-1]
                    dp[i][j] -= dp[left+1][right-1];
                }
            }
            dp[i][j] = (dp[i][j] + mod) % mod;
        }

    return dp[0][n-1];
}
};

总结:
花了大概两天的时间,做了这几道题目。这里提出一点要求,在做第一遍的时候觉得磕磕绊绊,思路或者代码不是那么好写的或者经常WA的,要收集整理过段时间从新从0开始做一遍。

你可能感兴趣的:(LeetCode题解,算法与数据结构)