最长回文子串的解法

1、暴力法
最容易想到的就是暴力破解,求出每一个子串,之后判断是不是回文,找到最长的那个。
求每一个子串时间复杂度O(N^2),判断子串是不是回文O(N),两者是相乘关系,所以时间复杂度为O(N^3)。

string findLongestPalindrome(string &s)  
{  
    int length=s.size();//字符串长度  
    int maxlength=0;//最长回文字符串长度  
    int start;//最长回文字符串起始地址  
    for(int i=0;i<length;i++)//起始地址  
        for(int j=i+1;j<length;j++)//结束地址  
        {  
            int tmp1,tmp2;  
            for(tmp1=i,tmp2=j;tmp1<tmp2;tmp1++,tmp2--)//判断是不是回文  
            {  
                if(s.at(tmp1)!=s.at(tmp2))  
                    break;  
            }  
            if(tmp1>=tmp2&&j-i>maxlength)  
            {  
                maxlength=j-i+1;  
                start=i;  
            }  
        }  
        if(maxlength>0)  
            return s.substr(start,maxlength);//求子串  
        return NULL;  
}  

2、动态规划
回文字符串的子串也是回文,比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串。这样最长回文子串就能分解成一系列子问题了。这样需要额外的空间O(N^2),算法复杂度也是O(N^2)。
首先定义状态方程和转移方程:
P[i,j]=0表示子串[i,j]不是回文串。P[i,j]=1表示子串[i,j]是回文串。
P[i,i]=1

P[i,j]{=P[i+1,j-1],if(s[i]==s[j])
=0 ,if(s[i]!=s[j])

string findLongestPalindrome(string &s)  
{  
    const int length=s.size();  
    int maxlength=0;  
    int start;  
    bool P[50][50]={false};  
    for(int i=0;i<length;i++)//初始化准备  
    {  
        P[i][i]=true;  
        if(i<length-1&&s.at(i)==s.at(i+1))  
        {  
            P[i][i+1]=true;  
            start=i;  
            maxlength=2;  
        }  
    }  
    for(int len=3;len<length;len++)//子串长度  
        for(int i=0;i<=length-len;i++)//子串起始地址  
        {  
            int j=i+len-1;//子串结束地址  
            if(P[i+1][j-1]&&s.at(i)==s.at(j))  
            {  
                P[i][j]=true;  
                maxlength=len;  
                start=i;  
            }  
        }  
    if(maxlength>=2)  
        return s.substr(start,maxlength);  
    return NULL;  
}  

3、中心扩展
中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)。
但是要考虑两种情况:
1、像aba,这样长度为奇数。
2、想abba,这样长度为偶数。

string findLongestPalindrome(string &s)  
{  
    const int length=s.size();  
    int maxlength=0;  
    int start;  

    for(int i=0;i<length;i++)//长度为奇数  
    {  
        int j=i-1,k=i+1;  
        while(j>=0&&k<length&&s.at(j)==s.at(k))  
        {  
            if(k-j+1>maxlength)  
            {  
                maxlength=k-j+1;  
                start=j;  
            }  
            j--;  
            k++;  
        }  
    }  

    for(int i=0;i<length;i++)//长度为偶数  
    {  
        int j=i,k=i+1;  
        while(j>=0&&k<length&&s.at(j)==s.at(k))  
        {  
            if(k-j+1>maxlength)  
            {  
                maxlength=k-j+1;  
                start=j;  
            }  
            j--;  
            k++;  
        }  
    }  
    if(maxlength>0)  
        return s.substr(start,maxlength);  
    return NULL;  
}  

4、Manacher法
Manacher法只能解决例如aba这样长度为奇数的回文串,对于abba这样的不能解决,于是就在里面添加特殊字符。我是添加了“#”,使abba变为a#b#b#a。这个算法就是利用已有回文串的对称性来计算的,具体算法复杂度为O(N),我没看出来,因为有两个嵌套的for循环。
具体原理参考这里
测试代码中我没过滤掉“#”。

#define min(x, y) ((x)<(y)?(x):(y))  
#define max(x, y) ((x)<(y)?(y):(x))  
string findLongestPalindrome3(string s)  
{  
    int length=s.size();  
    for(int i=0,k=1;i<length-1;i++)//给字符串添加 #  
    {  
        s.insert(k,"#");  
        k=k+2;  
    }  
    length=length*2-1;//添加#后字符串长度  
    int *rad=new int[length]();  
    rad[0]=0;  
    for(int i=1,j=1,k;i<length;i=i+k)  
    {  
        while(i-j>=0&&i+j<length&&s.at(i-j)==s.at(i+j))  
            j++;  
        rad[i]=j-1;  
        for(k=1;k<=rad[i]&&rad[i-k]!=rad[i]-k;k++)//镜像,遇到rad[i-k]=rad[i]-k停止,这时不用从j=1开始比较  
            rad[i+k]=min(rad[i-k],rad[i]-k);  

        j=max(j-k,0);//更新j  

    }  
    int max=0;  
    int center;  
    for(int i=0;i<length;i++)  
    {  
        if(rad[i]>max)  
        {  
            max=rad[i];  
            center=i;  
        }  
    }  
    return s.substr(center-max,2*max+1);  

}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中的代码展示了一种求解最长回文子串的暴力解法,即遍历字符串的所有子串,判断是否为回文,并记录最长的回文子串。但是这种方法的时间复杂度较高,不适用于较长的字符串。 Python 中有一种更优化的方法来求解最长回文子串,即中心扩展法。该方法的基本思想是从字符串的每个字符和每两个相邻字符之间展开,向两边扩展判断是否为回文子串。具体步骤如下: 1. 定义一个函数 expandAroundCenter,用于判断以某个中心点向两边扩展的回文子串的长度。 2. 遍历字符串,将每个字符和其相邻字符都作为中心点进行扩展,计算得到回文子串的最大长度。 3. 根据最大长度和中心点位置,确定最长回文子串的起始位置和结束位置。 4. 返回最长回文子串。 下面是基于中心扩展法的 Python 代码示例: ``` class Solution: def longestPalindrome(self, s): if len(s) < 2: return s start, end = 0, 0 for i in range(len(s)): len1 = self.expandAroundCenter(s, i, i) # 以一个字符为中心向两边扩展 len2 = self.expandAroundCenter(s, i, i+1) # 以相邻两个字符为中心向两边扩展 max_len = max(len1, len2) if max_len > end - start: start = i - (max_len - 1) // 2 end = i + max_len // 2 return s[start:end+1] def expandAroundCenter(self, s, left, right): while left >= 0 and right < len(s) and s[left] == s[right]: left -= 1 right += 1 return right - left - 1 s = "ac" S = Solution() result = S.longestPalindrome(s) print(result) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值