LeetCode05:最大回文子串

问题描述:给定一个字符串,找到字符串中的最长回文子串。
思路

  • 暴力求解

描述:枚举所有子串,判断是否为回文子串,并设置全局最大长度回文子串,在遍历过程中不断更新。
时间复杂度:o(n3)
空间复杂度:o(n)

  • 最长公共子串(二维打表)

描述:设置二维表记录原字符串与反转字符串对比情况,在比较过程中动态的记录回文子串的长度。
时间复杂度:o(n2)
空间复杂度:o(n2)(可通过一维复用达到o(n))

  • 动态规划

描述:与上面方法类似,设置二维表,但只用右上角部分,从主对角线往上分别表示长度为1…n的情况,其中1,2需初始化。
时间复杂度:o(n2)
空间复杂度:o(n)或o(n2)

  • 中心扩展算法

描述:考虑奇偶情况的2n - 1个位置(每个元素和元素之间的间隙位置),以每个位置作为中心点扩散,遍历找到最大子串。
时间复杂度:o(n2)
空间复杂度:o(1)

  • Manacher(马拉车算法)

描述:对中心扩展算法的优化,通过补充无关元素来达到统一奇偶的目的。添加一额外数组记录以该元素为中心的回文串长度,于是问题转变为如何更好地得出辅助数组。具体思想是通过回文映射,将问题分为三类情况
原字符串:a b c b d
变化后:
1. 正常情况
$ c $ a $ b $ a $ c $ b $
0 1 0 1 0 3
j’ C j R//j为遍历位置,C为中心,R为中心扩展边界
2.遍历位置加映射值的下标大于R
3.j’遇到左边界,由于j’扩展时受限,不能确定j的情况
4.j=R
除第一种情况外,后面三种都要使用中心扩展算法进行扩展
时间复杂度:o(n)
空间复杂度:o(n)

//暴力法(略)
//最长公共子串
class Solution {
public:
    string longestPalindrome(string s) {
        string str = s;
	    string maxstr = "";
	    int maxlen = 0;
	    int index;
	    int len = s.length();
	    vector<vector<int>>  var(len + 1, vector<int>(len + 1));
	    if (len == 1) return str;//字符串长度为1,直接返回
	    reverse(str.begin(), str.end());//将字符串反转,准备进行比较
	    if (str == s) return s;
	    for (int i = 0; i < len + 1; ++i){
		    for (int j = 0; j < len + 1; ++j){
			    if (i == 0 || j == 0){
				    var[i][j] = 0;//初始化二维表首行首列
			    }
			    else if (str[i - 1] == s[j - 1]){
				    var[i][j] = var[i - 1][j - 1] + 1;//若反转前后元素相同,值更新为左上角值加一
				    if (var[i][j] > maxlen && (i + j - var[i][j]) == len){
					    maxlen = var[i][j];//若记录值超过最大记录长度,更新最大长度
					    index = j;记录子串尾下标
				    }
			    }
			    else{
				    var[i][j] = 0;
			    }
		    }
	    }
	    if (maxlen){
            int begin = (index - maxlen) > 0 ? (index - maxlen):0;//找到最大回文子串的首下标
		    maxstr = s.substr(begin, maxlen);
	    }
	    return maxstr;
	}
};

//动态规划
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        int index, end, max = 1;
        if(len == 1 || len == 0)    return s;
        vector<vector<bool>> dp(len, vector<bool>(len));//构建二维数组
        for(int i = 0; i < len; ++i){
            dp[i][i] = 1;//单个元素必是回文
            if(i < len - 1 && s[i] == s[i+1]){
                dp[i][i+1] = 1;//两个元素回文判断
                max = 2;
                index = i;
            }
        }
        for(int l = 3; l <= len; ++l){//长度大于3的情况
            for(int start = 0; start + l - 1 < len; ++start){
                end = start + l - 1;
                if(dp[start + 1][end - 1] && s[start] == s[end]){//如果长度大的子串去掉两端未回文串且两端元素相同,二维表对应值置1
                    dp[start][end] = 1;
                    max = l;//由于越往后判断长度越大,所以更新值一定是最大值
                    index = start;
                }
            }    
        }
        return s.substr(index, max);
	}
};
//优化空间复杂度至o(n)
/*
a b c b d
        1 //i = 4, j = 4
      1 0 //i = 3, j = 4, 3
    1 0 0 //i = 2, j = 4, 3, 2
  1 0 1 0 //i = 1, j = ...,(当j = 3时需要用到上一步i=3,j=3的信息)
1 0 0 0 0//...  
*/
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        string maxstr = "";
        vector<bool> dp(len);//采用一维数组记录
        for(int start = len - 1; start >= 0; --start){//从最后一位字符开始
            for(int end = len - 1; end >= start; --end){
                dp[end] = s[start] == s[end] && (end - start < 2 || dp[end - 1]);
                if(dp[end] && end - start + 1 >= maxstr.length()){
                    maxstr = s.substr(start, end - start + 1);
                }
            }    
        }
        return maxstr;
	}
};

//中心扩展算法
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        string maxstr = "";
        for(int i = 0; i < len; ++i){
            int len1 = expendCenter(s, i, i);
            int len2 = expendCenter(s, i, i + 1);
            int maxlen = max(len1, len2);//找出相邻奇偶最大回文串长度
            if(maxlen > maxstr.length()){
                int left = i - (maxlen - 1)/2;//找到最大回文串左下标
                maxstr = s.substr(left, maxlen);
            }
        }
        return maxstr;
	}
private:
    int expendCenter(string s, int i, int j){
        int L = i;
        int R = j;
        while(L >= 0 && R <= s.length() && s[L] == s[R]){//回文扩散
            --L;
            ++R;
        }
        return R - L - 1;
    }    
};

//manacher算法
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        if(len == 0 || len == 1) return s;
        string s1;
        for(int i = 0; i < len; ++i){//补位统一奇偶
            s1 += '#';
            s1 += s[i];
        }
        s1 += '#';
        int len1 = s1.length();
        int jmap = 0;
        int C = 0;
        int R = 0;
        int maxlen = 1;
        int start = 0;
        vector<int> p(len1, 0);
        for(int j = 0; j < len1; ++j){
            if(j < R){
                jmap = 2 * C - j;//当前遍历位置关于C的映射
                p[j] = min(R - j, p[jmap]);//防止j+p[jmap]>R的情况
            }
            int left = j - p[j] - 1;//中心扩展左端点
            int right = j + p[j] + 1;
            while(left >= 0 && right <= len1 && s1[left] == s1[right]){//应对后三种情况进行中心扩展
                ++p[j];
                --left;
                ++right;
            }
            if(j + p[j] > R){//对R和C进行更新,保证j一直处于辐散区域
                R = j + p[j];
                C = j;
            }
            if(p[j] > maxlen){//维持最大回文子串,对应原数组
                maxlen = p[j];
                start = (j - maxlen)/2;
            }
        }
        return s.substr(start, maxlen);
    }    
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值