leetcode 5 最长回文子串 (暴力,manacher)

本文详细解析了LeetCode上经典题目——寻找最长回文子串的两种算法实现:暴力解法和Manacher算法(马拉车算法)。通过实例演示如何找到字符串中的最长回文子串,包括奇数和偶数长度的回文情况。

https://leetcode-cn.com/problems/longest-palindromic-substring/

题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

输入: “cbbd”
输出: “bb”

思路1
暴力,枚举回文中心点,向两边延伸直到不回文,统计最长的字串即可
代码

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.length();
        string ans;
        int l, r;
        for (int i = 0; i < n; i++) {
            l = i, r = i;
            while (s[l] == s[r]) {
                l--; r++;
                if (l < 0 || r == n) break;
            } l++; r--;
            if (r - l + 1 > ans.length()) ans = s.substr(l, r - l + 1);
            
            l = i - 1, r = i;
            if (l < 0) continue;
            while (s[l] == s[r]) {
                l--; r++;
                if (l < 0 || r == n) break;
            } l++; r--;
            if (r - l + 1 > ans.length()) ans = s.substr(l, r - l + 1);
        }
        return ans;
    }
};

思路2
学习了一下manacher算法,又叫马拉车算法
1. 首先解决奇数回文和偶数回文不一致的问题,给原字符串每个字符之间加个分隔符,这里用 ‘#’ ,并且头尾再加上两个不同的字符用来截止,以免数组越界。例如,字符串 “abc” ,处理后变成 “!#a#b#c#$” ,这样所有回文字串都是奇数长度,统一起来
2. 然后我们维护两个值,center,和 maxRmaxR表示字符串中各回文串能够延伸到的最远的位置,而 center是该子串的中心,这样我们可以分成两种情况
(1) 当前位置 i >= maxR,不知道dp[i],置为1,等待后续延伸
(2) 当前位置 i < maxR,我们找到 i 关于 center的对称点,也就是 center * 2 - i,因为是回文的,所以对称点在以 center为中心的子串以内的回文子串是和 i 点一样的,这时令 dp[i] = min(dp[center * 2 - i], maxR),不能超过了,因为对称点还可能包含center回文串以外的字串,所以只能取这之间的较小值

3. dp[i] 暂时的值确定了,接下来进行延伸,两端的字符一样 dp[i] 就++,直到不一样为止,这时候那两个不同的字符作用就出来了,不设两个不同的,比到后来会越界。
4. 最后更新 maxR
5. 遍历一遍长度数组,找到最长的下标,提取正确字符串

代码

class Solution {
public:
    string longestPalindrome(string s) {
        string ans;
        if (!s.length()) return ans;
        string ns = "*#";
        for (int i = 0; i < s.length(); ++i) {
            ns += s[i]; ns += '#';
        } ns += '-';
        vector <int> dp(ns.length());
        int maxR(0), center(0);

        for (int i = 1; i < ns.size() - 1; i++) {
            if (maxR > i) 
                dp[i] = min(dp[center * 2 - i], maxR - i);
            else dp[i] = 1;
            while (ns[i + dp[i]] == ns[i - dp[i]]) 
                dp[i]++;   
            if (dp[i] + i > maxR) {
                maxR = i + dp[i];
                center = i;
            }
        }
        
        int a(0), b(0);
        for (int i = 0; i < dp.size(); i++) {
            if (dp[i] > b) a = i, b = dp[i];
        }
        for (int i = a - dp[a] + 1; i < a + dp[a]; i++) 
            if (ns[i] != '#') ans += ns[i];
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值