求最长回文子串

9 篇文章 0 订阅

字符处理编程笔记一
1.最长回文子串
1.最长回文子串
题目:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
1
2
3
示例 2:

输入: "cbbd"
输出: "bb"
1
2
解法一:暴力解法
1.两重循环遍历所有子串;
2.判断回文子串。

时间复杂度:O(n3) O(n^3)O(n 
3
 )

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main() {

    string str;

    getline(cin, str);

    int max = 0;
    int start = 0;

    for (int i = 0; i < str.size(); i++)
    {
        for (int j = i; j < str.size(); j++)
        {
            int flag = 1;
            for (int k = 0; k < (j - i + 1) / 2; k++)
            {
                if (str[k + i] != str[j - k])
                {
                    flag = 0;
                    break;
                }
            }

            if (flag && j - i + 1 > max)
            {
                max = j - i + 1;
                start = i;
            }
        }
    }
    cout << str.substr(start, max) << endl;
    
    return 0;
}

解法二:中心扩展法
1.遍历一遍字符串,将字符作为子串中心;
2.从子串中心从左右同时遍历,判断是否相同。
(奇子串n个和偶子串n-1个,二者区别在于需考虑是否用中心的字符)
时间复杂度:O(n2) O(n^2)O(n 
2
 )

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

int expand(string s, int left, int right)
{
    int l = left;
    int r = right;
    while (l >= 0 && r < s.size()&&s[l] && s[r] && s[l] == s[r])
    {
        l--;
        r++;
    }
    return r - l - 1;
}

string findLongestPalindrome(string s)
{
    if (s.empty() || s.size() < 1)
        return s;
    int start = 0;
    int end = 0;
    int len = 0;
    for (int i = 0; i < s.size(); i++)
    {
        int len1 = expand(s, i, i);
        int len2 = expand(s, i, i + 1);
        len = max(len1, len2);
        if (len > end - start)
        {
            start = i - (len - 1) / 2;
            end = i + len / 2;
        }
    }
    return s.substr(start, end - start + 1);
}

int main() {

    string str;

    getline(cin, str);

    //中心扩展法
    cout << findLongestPalindrome(str) << endl;
    return 0;
}

解法三:动态规划
状态转移方程:
dp[j][i]=⎧⎩⎨true,str[j]==str[i],str[j]==str[i]&&dp[j+1][i−1],j=i;j−i<2j−1>=2 dp[j][i]=\left\{\begin{aligned}&true ,&j=i;\\&str[j]==str[i] ,&j-i < 2\\&str[j]==str[i]\&\&dp[j+1][i-1],&j-1 >=2\end{aligned}\right.dp[j][i]= 





​    
  
​    
  
true,
str[j]==str[i],
str[j]==str[i]&&dp[j+1][i−1],
​    
  
j=i;
j−i<2
j−1>=2
​    
 
时间复杂度:O(n2) O(n^2)O(n 
2
 )

string longestPalindrome(string s) {
        const int n=s.size();
        bool dp[n][n];
        fill_n(&dp[0][0],n*n,false);
        int max_len=1; //保存最长回文子串长度
        int start=0;//保存最长回文子串起点
        for(int i=0;i<s.size();++i)
        {
            for(int j=0;j<=i;++j)
            {
                if(i-j<2)
                    dp[j][i]=(s[i]==s[j]);
                else
                    dp[j][i]=(s[i]==s[j] && dp[j+1][i-1]);
                if(dp[j][i] && max_len<(i-j+1))
                {
                    max_len=i-j+1;
                    start=j;
                }
            }
        }
        return s.substr(start,max_len);
    }

解法四:马拉车算法(Manacher)
1.扩展字符串均为奇回文,避免奇回文和偶回文两次处理;
2.建立数组,存储以str[i]为中心的最长回文半径;
3.确定最长回文半径和最长回文子串长度之间的关系: maxLen=p[i]−1 maxLen = p[i] - 1maxLen=p[i]−1;
4.首位添加两个特殊符号,最长回文子串的起始索引index=(i−p[i])÷2 index = (i - p[i])\div 2index=(i−p[i])÷2

5.设置两个辅助变量:
mx: mx:mx:表示当前能延伸到最右端的回文子串的最右端点;
id: id:id:表示当前能延伸到最右端的回文子串的中心端点。
p[i]=mx>i?min(p[2×id−i],mx−i):1 p[i] = mx > i ? min(p[2 \times id - i], mx - i) : 1p[i]=mx>i?min(p[2×id−i],mx−i):1
由于回文子串中心对称,当p[i]+i<mx p[i]+i <mxp[i]+i<mx时,p[i]==p[2×id−i] p[i] ==p[2 \times id - i]p[i]==p[2×id−i],否则,p[i]==mx−i p[i] == mx - ip[i]==mx−i,而超过mx的部分需要继续遍历得到。
时间复杂度:O(n) O(n)O(n)

string rebuildStr(string &s)
{
    string newStr(s.length() * 2 + 2,' ');
    int k = 0;
    newStr[k++] = '@';
    for (int i = 0; i < s.length(); i++)
    {
        newStr[k++] = '#';
        newStr[k++] = s[i];
    }
    newStr[k++] = '#';
    newStr[k] = '*';

    return newStr;
}

int manacher(string &s)
{
    string str = rebuildStr(s);
    int len = str.size();
    vector<int> p(len, 0);
    int mx = 0,id = 0;//mx为最右边,id为中心点

    //最长回文子串的长度和索引
    int maxLen = 0;
    int index = 0;


    for (int i = 1; i < str.size(); i++) {
        p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

        while (str[i + p[i]] == str[i - p[i]]) p[i]++;//向左右扩展
        
        //更新mx
        if (p[i] + i > mx) {
            mx = p[i] + i;
            id = i;
        }

        if (maxLen < p[i] - 1)
        {
            maxLen = p[i] - 1;
            index = i;
        }
    }

    int start = (index - maxLen) / 2;
    string subStr = s.substr(start, maxLen);
    return maxLen;
}
————————————————
版权声明:本文为CSDN博主「AraSauHan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42018838/article/details/103929222

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值