求解最长的回文子串

本文介绍了三种查找字符串中最长回文子串的方法:暴力法、对称求解法及Manacher算法。其中Manacher算法通过插入特殊字符解决奇偶长度问题,并利用回文半径数组减少重复计算。

1.暴力法

找出所有子串,并且判断是否是回文子串,并且记录最长长度,算法复杂度为O(n^3),强烈不推荐。

2.对称求解法

遍历字符串每个位置,从每个位置向两边扩散,判断是否对称,算法复杂度为O(n^2),但是需要考虑长度为奇偶两种情况。

示例代码:

 public int longestPalindrome(String str) {
int length=str.length();
if(str==null||length<1) {
return 0;
}
int result=0;
int max=0;
//遍历所有位置
for(int i=0;i<length;i++) {
//考虑str长度为奇数的情况,j为两边移动的距离
for(int j=0;j<=i&&i+j<length;j++) {
if(str.charAt(i-j)!=str.charAt(i+j)) {
break;
}
max=j*2+1;
}
if(max>result) {
result=max;
}
//考虑str长度为偶数的情况,j为移动的距离
for(int j=0;j<=i&&i+j+1<length;j++) {
if(str.charAt(i-j)!=str.charAt(i+j+1)) {
break;
}
max=j*2+2;
}
if(max>result)
result=max;
}
return result;

}

3.Manacher算法

从解法2我们可以发现仍有很多子串被重复判断,所以就引入了Manacher算法。

第一个不同:首先Manacher算法为了解决奇偶问题在原字符串中插入了#符号,将字符串长度统一为奇数,例如将字符串abcbae扩充为#a#b#c#b#a#e#

第二个不同:Manacher算法引入了一个回文半径,即用一个数组保存每个字符的回文半径,回文半径初始化为1,(本身),求解回文半径方法见代码注释。

 public  int longestPalindrome(String str) {
    //扩充字符串
    StringBuilder str2 = new StringBuilder();
    str2.append('#');
    for (int i = 0; i < str.length(); i ++) {
        str2.append(str.charAt(i));
        str2.append('#');
    }
    //回文半径数组
    int [] rad = new int[str2.length()];
    // right表示已知的最长回文子串最右边缘坐标
    int right = -1;
    // center表示已知的最长回文子串中点坐标
    int center = -1;
    
    for (int i = 0; i < str2.length(); i ++) {
        //初始回文半径1,自己本身
        int r = 1;
        // 找到i的关于center对称位置j,j=2*center-i
        //如果对称位置的rad小于right-i,说明以j为中心的回文子串在已知最长回文子串中,由对称性可知,rad[i]=rad[j]
        //否则i的回文半径就是right-i,所以回文半径就是right-i,所以就相当于取两个的最小值,这种情况就需要再向两边进行匹配
        //进行这个判断的原因是为了省去重复字符串扫描
        if (i <= right) {
            r = Math.min(right-i, rad[2 * center - i]);
        }
        // 向两边进行匹配
        while (i - r >= 0 && i + r < str2.length() && str2.charAt(i - r) == str2.charAt(i + r)) {
            r++;
        }
        // 如果扫描结束后位置大于right,需要更新right和center
        if (i + r - 1> right) {
            right = i + r - 1;
            center = i;
        }
        rad[i] = r;
    }
    
    // 扫描最大长度
    int result = 0;
    for (int r : rad) {
        if (r > result) {
            result = r;
        }
    }
    return result - 1;
}

求解最长回文子串是一个经典的算法问题,其目的是在一个字符串中找到最长的那个部分,这部分从前往后读和从后往前读是一样的。以下是几种常见的解决方法: ### 1. 暴力法 暴力枚举所有可能的子串,并逐个检查它们是否为回文。虽然这种方法直观易懂,但是效率很低,在长度为n的情况下需要O(n^3)的时间复杂度。 ```python def is_palindrome(s): return s == s[::-1] def longestPalindrome_brute_force(s: str) -> str: max_len = 0 res = "" for i in range(len(s)): for j in range(i, len(s)): if (j-i+1 > max_len and is_palindrome(s[i:j + 1])): max_len = j - i + 1 res = s[i:j + 1] return res ``` ### 2. 动态规划法 利用动态规划的思想可以将时间复杂度降低到O(n&sup2;),并且只需要O(n&sup2;)的空间来存储中间结果。该方法通过构建一张二维表格dp[][]来进行状态转移计算。 ```python def longestPalindrome_dynamic_programming(s: str) -> str: n=len(s) dp=[[False]*n for _ in range(n)] ans="" # 遍历每个起点i及其之后的所有终点j for l in range(n): for i in range(n-l): j=i+l # 如果两端字符相等,则进一步判断内部情况 if s[i]==s[j]: if j-i<=1 or dp[i+1][j-1]: # 单个字母/相邻两个相同 或者 剥离最外层后的串也是回文 dp[i][j]=True if j-i+1>len(ans): ans=s[i:j+1] return ans ``` 另外还有Manacher算法可以在更高效地线性时间内解决问题,它通过对原字符串做预处理并巧妙地维护中心对称特性使得只需一次遍历就能得出答案。由于此方法相对较为复杂这里不做详细展开。 以上就是关于“如何寻找给定字符串中最长连续回文序列”的介绍啦!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值