回文串总结

一、动态规划

1.模板:最长回文子串

1)题目

给你一个字符串 s,找到 s 中最长的回文子串。

2)思路

  • 动归:处理出i到j是否是回文串的数组,更新记录最长的回文子串长度。时间复杂度恒为n方。
  • 中心扩展:以每个字符为中心进行扩展回文字符串,时间复杂度最坏为n方,最好为n。

3)做法

  • 动归做法
class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        int res = 0;
        int start = 0;
        bool dp[n + 1][n + 1];//表示以i为起点,j为终点的字符串是否是回文串
        //从后往前遍历j,用到dp[i+1][j-1]需要先更新i更大的状态
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i ; j < n; j++) {
                if (j - i <= 1) dp[i][j] = s[i] == s[j];
                else dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]);
                // 记录答案
                if(dp[i][j] && j - i + 1 > res) {
                    res = max(res, j - i + 1);
                    start = i;
                }
            }
        }
        return s.substr(start, res);
    }
};
  • 中心扩展做法
class Solution {
public:
    pair<int, int> expand(const string& s, int left, int right) {
        //对象传引用,大大提高空间效率
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            left--;
            right++;
        }
        return {left + 1, right - 1};
    }

    string longestPalindrome(string s) {
        int start = 0, end = 0;
        for (int i = 0; i < s.size(); i++) {
            auto [left1, right1] = expand(s, i, i);
            auto [left2, right2] = expand(s, i, i + 1);
            if (right1 - left1 > end - start) {
                start = left1;
                end = right1;
            }
            if (right2 - left2 > end - start) {
                start = left2;
                end = right2;
            }
        }
        return s.substr(start, end - start + 1);
    }
};

2.分割回文串

1)题目

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

2)思路

先处理出i到j是否是回文串的数组,然后用dfs去搜索所有答案

3)做法

class Solution {
public:
    bool dp[20][20];
    int n;
    vector<vector<string>> res;
    vector<string> ans;

    void dfs(string s, int i) {
        int j;
        for (j = i; j < n; j++) {
            if (dp[i][j]) {
                ans.push_back(s.substr(i, j - i + 1));
                if (j == n - 1) {
                    res.push_back(ans);
                }
                else dfs(s, j + 1);
                ans.pop_back();
            }
        }
    }

    vector<vector<string>> partition(string s) {
        n = s.size();
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (j - i <= 1) dp[i][j] = s[i] == s[j];
                else dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]);
            }
        }
        dfs(s, 0);
        return res;
    }
};

3.分割回文串II

1)题目

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文。
返回符合要求的 最少分割次数 。

2)思路

先处理出i到j是否是回文串的数组,然后用f[i]记录以i结尾的字符串最少分割次数。
f[i] = min(f[j]) + 1, 0<=j<=I。

3)做法

class Solution {
public:
    int dp[2010][2010];
    int n;
    int minCut(string s) {
        n = s.size();
        int res = 0;
        //处理回文串数组
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (j - i <= 1) dp[i][j] = s[i] == s[j];
                else dp[i][j] = dp[i + 1][j - 1] && (s[i] == s[j]);
            }
        }

        int f[2010];//表示以i结尾的字符串最少分割次数
        for (int i = 1; i < n; i++) f[i] = INT_MAX;
        f[0] = 0;
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= i; j++) { //遍历以s[i]结尾的回文子串的起点
                if(dp[j][i]) {
                    if(j) {
                        if(f[j - 1] < INT_MAX) f[i] = min(f[i], f[j - 1] + 1);
                    }
                    else f[i] = 0;
                }
            }
        }
        return f[n - 1];
    }
};

4.回文子串个数

1)题目

Given a string s, return the number of palindromic substrings in it.

2)思路

  • 动归:处理出i到j是否是回文串的数组,遍历第二遍记录回文串的个数。
  • 中心扩展:以每个字符为中心进行扩展回文字符串,每个字符扩展完成统计长度。

3)做法

class Solution {
public:
    pair<int, int> expand(const string& s, int left, int right) {
        //对象传引用,大大提高空间效率
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            left--;
            right++;
        }
        return {left + 1, right - 1};
    }

    int countSubstrings(string s) {
        int res = 0;
        for (int i = 0; i < s.size(); i++) {
            auto [left1, right1] = expand(s, i, i);
            auto [left2, right2] = expand(s, i, i + 1);
            res += (right1 - left1) / 2 + 1 + (right2 - left2 + 1) / 2;
        }
        return res;
    }
};

二、valid Palindrome

1.125. Valid Palindrome

1)题目

Given a string s, return true if it is a palindrome, or false otherwise.

2)思路

主要处理非字符符号和大小写

3)做法

class Solution {
public:
    bool isPalindrome(string s) {
        string res;
        for (auto c: s) {
            if (isalpha(c)) res += toupper(c);
            else if (isdigit(c)) res += c;
        }
        for (int i = 0; i < res.size() / 2; i++) {
            if (res[i] != res[res.size() - i - 1]) return false;
        }
        return true;
    }
};

2.680. Valid Palindrome II

1)题目

Given a string s, return true if the s can be palindrome after deleting at most one character from it.

2)思路

每个不符合条件的点位,尝试两种删除方法

3)做法

class Solution {
public:
    bool validPalindrome(string s) {
        for (int i = 0; i < s.size() / 2; i++) {
            int j = s.size() - i - 1;
            if (s[i] != s[j]) {
                if (originalValid(s, i + 1, j) || originalValid(s, i, j - 1)) return true;
                else return false;
            }
        }
        return true;
    }
    
    bool originalValid(string &s, int i, int j) {
        for (; i < j; i++, j--) {
            if (s[i] != s[j]) return false;
        }
        return true;
    }
};

3.1216. Valid Palindrome III

1)题目

Given a string s and an integer k, return true if s is a k-palindrome.
A string is k-palindrome if it can be transformed into a palindrome by removing at most k characters from it.

2)思路

dp[i][j]代表从i到j最少删除几个字符就能变成回文串,最后判断dp[0][n-1]是否小于k

3)做法

O(n^2) space

class Solution {
public:
    bool isValidPalindrome(string s, int k) {
        int n = s.size();
        int f[n][n];
        memset(f, 0, sizeof f);
        for (int i = n - 2; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                if (s[i] == s[j]) f[i][j] = f[i + 1][j - 1];
                else f[i][j] = min(f[i][j - 1], f[i + 1][j]) + 1;
            }
        }
        return f[0][n - 1] <= k;
    }
};

O(n) space

class Solution {
public:
    bool isValidPalindrome(string s, int k) {
        int n = s.size();
        int f[n];
        memset(f, 0, sizeof f);
        int temp, prev;
        for (int i = n - 2; i >= 0; i--) {
            prev = 0; // f[i + 1][j - 1]
            for (int j = i + 1; j < n; j++) {
                temp = f[j]; // f[i + 1][j]
                if (s[i] == s[j]) f[j] = prev;
                else f[j] = min(f[j - 1], f[j]) + 1;
                prev = temp;
            }
        }
        return f[n - 1] <= k;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值