leetcode-7:回文串问题

回文串问题

解释一下为什么会记录刷题过程。记得研一为了算法课刷题,曾经在本子中记录过思路和代码。这些算法思想,平常也没机会使用,长时间不用,忘得比较快,再加上本子不易保存和现在电子阅读的普及,所以就导致复习不好复习,还得从头再来,所以这次刷题就记录下来,发在csdn和知乎,以便随时观看。

最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

思路: 最长回文子串问题的解决方法有常见的O(n2)O\left(n^{2}\right),还有神奇的O(n)O\left(n\right),被称为马拉车算法。对于O(n2)O\left(n^{2}\right)算法,是考虑到回文串的对称性,每次循环选择一个中心,左右扩展,直到左右字符不再相等。考虑到字符串长度的奇偶性,为了全面寻找最长回文串,需要从一个字符开始扩展,或者从两个字符间开始扩建,有2n-1个中心点。

class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        if(len==0) return "";
        int left=0, right=0;
        for(int i=0;i<len;i++){
            int len_1 = expand(s, i, i);
            int len_2 = expand(s, i, i+1);
            int len = len_1 > len_2 ? len_1 : len_2;
            if((right - left) < len){
                left = i - (len - 1) / 2;
                right = i + len / 2;
            }
        }
        return s.substr(left, (right - left + 1));
    }
    int expand(string s, int left, int right){
        while(left>=0&&right<s.length()&&s[left]==s[right]){
            left--;
            right++;
        }
        return right - left - 1;
    }
};

马拉车算法(Manacher’s Algorithm)

马拉车算法将时间复杂度降到了O(n)O\left(n\right),但是空间复杂度为O(n)O\left(n\right)。上述的中心扩展法的空间复杂度为O(1)O\left(1\right),用空间换取时间。

中心扩展的时候需要考虑到字符串奇偶性,假设字符串长度为nn,通过加入n+1n+1个分隔符使得字符串长度为奇数2n+12n+1,将“babad”处理成“#b#a#b#a#d#"。

设置目前回文串的对称中心点为Center,回文半径长度为Radius,右边到达最远为Right=Center+Radius,当前点为iiii关于Center的对称点为i_mirror=2Centerii\_mirror = 2 *Center - i。记录每个点回文字符串半径的矩阵为PP

我们需要根据i和Right的大小来计算P[i_mirror]P[i\_mirror]

  • i<Righti < Right时,如果i+P[i_mirror]>=Ri + P[i\_mirror] >= R,那么可以根据回文串对称性得到P[i]=P[i_mirror]P[i] = P[i\_mirror];否则,超越界限,所以需要取min(P[i_mirror],Ri)min(P[i\_mirror], R-i),然后再左右扩展

  • 如果i>Ri > R,此时镜像预测不起作用,P[i]=0P[i] = 0,需左右扩展。

class Solution {
public:
    string pre_process(string s){
        int len = s.length();
        len = 2 * len + 1;
        string str="";
        for(int i=0;i<len;i++){
            if(i%2==0){
                str += '#';
            }else{
                str += s[i/2];
            }
        }
        return str;
    }
    string longestPalindrome(string s) {
        string str = pre_process(s);
        int len = str.length();
        vector<int> P(len, 0);
        int c = 0, r = 0;
        int max_length = INT_MIN;
        int max_index = 0;
        for(int i=0;i<len;i++){
            int i_mirror = 2 * c - i;
            if(r>i){
                P[i] = min(r-i, P[i_mirror]);
            }
            while(((i+P[i])<len)&&((i-P[i])>=0)&&str[i+P[i]]==str[i-P[i]]){
                P[i]++;
            }
            if((i+P[i]) > r){
                c = i;
                r = i+P[i];
            }
            if(max_length < P[i]){
                max_index= i;
                max_length = P[i];
            }
        }
        return s.substr((max_index - max_length + 1)/2, max_length-1);
    }
};

回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

思路: 简单暴力,我们搜索每一个中心点。假设字符串长度为nn,按照扩展算法的描述,有2n+12*n+1中心,对每一中心进行扩展,如果是回文串,则记录下来。

class Solution {
public:
    int countSubstrings(string s) {
        int len = s.length();
        len = len*2-1;
        int left=0,right=0;
        int num = 0;
        for(int i=0;i<len;i++){
            left = i/2;
            right = left + i%2;
            while(left>=0&&right<len&&s[left]==s[right]){
                left--;
                right++;
                num++;
            }
        }
        return num;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章