数据结构与算法-1.最长回文子串

1、最长回文子串

题目

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

示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母(大写和/或小写)组成

1.0、两边向中间缩进(暴力)

这个其实就是暴力解法的优化一点点的玩意儿。

string longestPalindrome0(string s)
{
    int size = s.size();
    string result = "";
    int leftIndex, rightIndex;

    for (int i = 0; i < size; i++)          //size-i为需要验证子串的长度
    {
        for (int j = 0; j <= i; j++)        //i+1为需要验证的子串的个数,j为开始验证的下标
        {
            leftIndex = j;
            rightIndex = j + size - i - 1;
            bool flag = true;
            while (leftIndex < rightIndex && flag == true)
            {
                if (s[leftIndex] != s[rightIndex])
                    flag = false;
                leftIndex++;
                rightIndex--;
            }
            if (flag == true)
                return s.substr(j, size - i);
        }
    }
    return result;
}

时间复杂度:O(n三方)

空间复杂度:O(1)

1.1、动态规划

(这玩意儿实在是不好写,就不写了_)

状态转移方程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HKRaJAUc-1634992571138)(image-20211023181351022.png)]

边界条件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8MV6rQRZ-1634992571141)(image-20211023181426492.png)]

string longestPalindrome1(string s)
{
    int size = s.size();
    if (size < 2)return s;
    string result;
    int maxLength = 1;
    int beginIndex = 0;
    vector<vector<bool>> dp(size, vector<bool>(size, false));       //创建size*size大小的状态保存数组,dp[i][j]表示i到j的字符串是否为回文
    for (int i = 0; i < size; i++)                                  //单个字符肯定为回文,标识为true
        dp[i][i] = true;

    for (int length = 2; length <= size; length++)                  //length为子字符串长度,从2开始,是因为1就是1啊。。。
    {
        for (int leftIndex = 0; leftIndex < size; leftIndex++)      //子字符串的左下标
        {
            int rightIndex = leftIndex + length - 1;
            if (rightIndex >= size)break;

            if (s[leftIndex] == s[rightIndex])
            {
                if (rightIndex - leftIndex < 3)                     //对应边界条件,s[i]==s[j],i+1==j时满足回文长度为2,i+2==j时满足回文长度为3         
                    dp[leftIndex][rightIndex] = true;
                else
                    dp[leftIndex][rightIndex] = dp[leftIndex + 1][rightIndex - 1];
                //保证上式成立的关键是,length由小及大,假设现在length为5,那么dp[leftIndex + 1][rightIndex - 1]在lengrh为3中就已确定
            }
            if (dp[leftIndex][rightIndex] && length > maxLength)
            {
                maxLength = length;
                beginIndex = leftIndex;
            }
        }
    }
    return s.substr(beginIndex, maxLength);
}

时间复杂度:O(n平方)

空间复杂度:O(n平方)

和暴力解法对比一下,其实就是用一个n平方空间把暴力解法中的那个while换掉了

1.2、中心扩展法

如果字符串是奇数(bab型),可以以中间某个字符为起点,向两边开始检查是否为回文,但如果是偶数(baab型)这样做就是错的

。下面是两种解决方法

将连续的字符看作是一个整体

string longestPalindrome2(string s)
{
    string result;
    int size = s.size();
    int leftIndex, rightIndex;
    int maxLength = 0;
    for (int i = 0; i < size; i++)
    {
        int tempLength = 1;
        leftIndex = i - 1;
        rightIndex = i + 1;
        while (i + 1 < size && s[i + 1] == s[i])        //将连续的字符归为一个整体
        {
            i++;
            rightIndex = i + 1;
            tempLength++;
        }

        while (leftIndex >= 0 && rightIndex < size)     //开始向两边检测
        {
            if (s[leftIndex] != s[rightIndex])
                break;
            leftIndex--;
            rightIndex++;
            tempLength += 2;
        }
        if (tempLength > maxLength)
        {
            maxLength = tempLength;
            result = s.substr(leftIndex + 1, tempLength);
        }
    }
    return result;
}

时间复杂度:O(n平方)

空间复杂度:O(1)

一般都用这个,简单,还快,还不占内存

在字符串中间插入 # ,bab就变成了#b#a#b#,baab就变成了#b#a#a#b#,看一看是不是都是奇数了

string longestPalindrome2(string s)
{
    string sourceString;
    for (auto& i : s)
    {
        sourceString.push_back('#');
        sourceString.push_back(i);
    }
    sourceString.push_back('#');

    string result;
    string tempresult;
    int size = sourceString.size();
    int leftIndex, rightIndex;
    int maxLength = 0;
    for (int i = 0; i < size; i++)
    {
        int tempLength = 1;
        leftIndex = i - 1;
        rightIndex = i + 1;

        while (leftIndex >= 0 && rightIndex < size)
        {
            if (sourceString[leftIndex] != sourceString[rightIndex])
                break;
            leftIndex--;
            rightIndex++;
            tempLength += 2;
        }
        if (tempLength > maxLength)
        {
            maxLength = tempLength;
            tempresult = sourceString.substr(leftIndex + 1, tempLength);
        }
    }

    for (auto& i : tempresult)
        if (i != '#')
            result.push_back(i);
    return result;
}

时间复杂度:O(n平方)

空间复杂度:O(n)

不用考虑奇,偶,占用空间可以接受,也比较常用

1.3、其他

Manacher 算法,时间复杂度:O(n),空间复杂度:O(n),有点难,我也不会,想学习的可以去百度(-_-!)。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值