【leetcode】5. Longest Palindromic Substring

230 篇文章 0 订阅

5. Longest Palindromic Substring

Medium

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

试题链接:https://leetcode.com/problems/longest-palindromic-substring/

 

法一:动态规划 O(n^2)

维护二维数组dp,dp[i][j]表示字符串区间[i, j]是否为回文串。

当i = j时,只有一个字符,肯定是回文串。

当i = j + 1,说明是相邻字符,判断s[i]是否等于s[j]。

当i - j >= 2,即i和j不相邻时,2个条件即为回文:1)s[i]和s[j]相等,2)dp[j + 1][i - 1]若为真。

综上写出递推式如下:

dp[i, j] = 1                                             if i == j

           = s[i] == s[j]                               if j = i + 1

           = s[i] == s[j] && dp[i + 1][j - 1]    if j > i + 1      

 

法二:对称中心向两边扩散

2.1.暴力法 O(n^2)

注意:奇数长度子串和偶数长度字串的对称中心不同,便利时二者都要讨论。

 

2.2.马拉车法 O(n)

第一步:改造原字符串,将奇数和偶数统一成奇数问题。

原始:1 2 2 1 2 2                              // 记为str

改造:# 1 # 2 # 2 # 1 # 2 # 2 # 

 

第二步:改造串前加上一个字符,为了方便计算最后结果的下标。

改造:$ # 1 # 2 # 2 # 1 # 2 # 2 #      // 记为数组t

 

第三步:维护数组p,p[i]记录以t[i]为中心的最长回文半径。

t:$ # 1 # 2 # 2 # 1 # 2 # 2 # 

p:- 1 2 1 2 5 2 1 6 1 2 3 2 1

找规律,发现:

str回文长度 = t回文半径 - 1

str回文起始位置 = (t回文中心位置 - t回文半径) / 2

 

第四步:重点在于p数组如何求

 从左往右计算数组P[ ], Mi为当前取得最大回文串的中心位置,而R是最大回文串能到达的最右端的值。

  1)当 i <=R时,找到点 i 关于 Mi 的对称点 j ,其值为 j= 2*Mi-i 。因点 j 、i 在以Mi 为中心的最大回文串的范围内([L ,R]),

       a)那么如果P[j] <R-i (同样是L和j 之间的距离),说明,以点 j 为中心的回文串没有超出范围[L ,R],由回文串的特性可知,从左右两端向Mi遍历,两端对应的字符都是相等的。所以P[ j ]=P[ i ],如下图:

 

     b)如果P[ j ]>=R-i (即 j 为中心的回文串的最左端超过 L),如下图所示。即,以点 j为中心的最大回文串的范围已经超出了范围[L ,R] ,这种情况,以点 j 为中心的回文串的最左端超过L,那么在[ L, j ]之间的字符肯定能在( j, Mi ]找到相等的,由回文串的特性可知,P[ i ] 至少等于R- i,至于是否大于R-i(图中红色的部分),我们还要从R+1开始一一的匹配,直达失配为止,从而更新R和对应的Mi以及P[ i ]。

  2)当 i > R时,如下图。这种情况,没法利用到回文串的特性,只能老老实实的一步步去匹配。

代码:

class Solution {
public:
    string longestPalindrome(string s) {
        string t = "$#";
        for(int i = 0; i < s.size(); ++i){
            t += s[i];
            t += "#";
        }
        t += "#";
        
        int p[2002];
        int mi = 1, r = 1, resmi = 1, reslen = 1;
        p[1] = 1;
        for(int i = 2; i < s.size() * 2 + 2; ++i){
            p[i] = (i < r)? min(p[mi * 2 - i], r - i): 1;
            while (t[i + p[i]] == t[i - p[i]]) ++p[i];
            if (i + p[i] > r){
                mi = i;
                r = i + p[i];
            }
            if (reslen < p[i]){
                resmi = i;
                reslen = p[i];
            }
        }
        return s.substr((resmi - reslen) / 2, reslen-1);
    }
};

马拉车方法参考链接:https://www.cnblogs.com/love-yh/p/7072161.html

http://www.cnblogs.com/grandyang/p/4475985.html

 

 

本篇参考:http://www.cnblogs.com/grandyang/p/4464476.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值