算法Week08.01 - LeetCode 5. Longest Palindromic Substring

第4、5周的时候一直在赶DDL,写的题都没时间上来写题解…
懈怠久了后来连题都没写更不要说题解了…orz

少年啊少年啊,快鼓起劲来~

题目大意

寻找字符串中最长的回文子串。

随机 Pick 到这题时我的内心是拒绝的,最开始看到 Palindromic 的时候一脸懵逼,觉得题目中肯定会解释这个词意吧,结果…/微笑脸

百度了一番,才知道是“回文”:

“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。

个人题解

题意上没有太大的障碍,而且除了暴力也没有什么特别好的想法;不过,暴力也可以有一点小技巧。

暴力的思路很简单,用长度的奇偶对回文串进行分类,有如下两种形式:

奇偶回文串

考察它们的中心点,奇回文串有唯一的一个中心点,而偶回文串则有两个中心点。

那么对于字符串中的每一个位置,它可以是一个奇回文串的中心 a (至少它本身可以构成一个回文串),还可能是一个偶回文串的左中心 b 或右中心 c;也即是说,每个位置至少对应一个子回文串,至多对应三个子回文串。

这里写图片描述

取每个位置对应的子回文串中具有最大长度者作为该位置对应的主子回文串,那么最长子回文串必定在这些主子回文串中。

对于字符串的任意一个位置,得到它的主子回文串最多需要遍历字符串的所有位置,即是对于一个位置复杂度为 O(n) ;求出所有位置上的主子回文串的复杂度为 O(n2)。

最后考虑优化,使用 MAX 记录当前最大的长度,idx表示当前位置下标。那么如果 (idx + 1) * 2 <= MAX 或者 (size - idx) * 2 <= MAX,显然就没有继续求下去的必要了,因为该位置的左端或者右端已经限定它不可能获得更长的回文串。

除此之外,因为一个偶回文串存在两个中心点,那么对这两个中心点所在位置进行考察时,会得到相同的子回文串,也即是存在重复;为了避免重复的考察,从字符串的中心 (size / 2) 出发,对于中心左边的位置只需要考虑作为奇中心或右中心点(作为左中心点的情况如果存在,那么前一个位置作为右中心点一定已经考察过该情况了);对于中心右边的位置只需要考虑作为奇中心或左中心点。

源代码

class Solution {
public:
    string longestPalindrome(string s) {
        int center = s.size() / 2;
        int target_a = center;
        int target_b = center;
        int a = getLongestPalidrome(s, center, 0, true, target_a);
        int b = getLongestPalidrome(s, center, 0, false, target_b);

        if (a >= b) {
            return s.substr(target_a - a / 2, a);
        } else {
            if (b % 2 == 0)
                return s.substr(target_b - b / 2 + 1, b);
            else
                return s.substr(target_b - b / 2, b);
        }
    }

    static int getLongestPalidrome(const string &s, int center, int MAX, bool direction, int &target) {
        if (center < 0 || center >= s.size()) return MAX;

        if (direction == true) {                // LEFT == true
            if ((center + 1) * 2 < MAX) return MAX;   // when the length can no longer be larger than MAX

            int cnt_exact = 1;                  // exactly in the center
            for (int i = 1; center + i < s.size() && center - i >= 0; i++) {
                if (s[center + i] == s[center - i]) cnt_exact += 2;
                else break;
            }

            int cnt_right = 0;                  // right
            if (center - 1 >= 0 && s[center - 1] == s[center]) {
                cnt_right = 2;
                for (int i = 1; center + i < s.size() && center - 1 - i >= 0; i++) {
                    if (s[center + i] == s[center - 1 - i]) cnt_right += 2;
                    else break;
                }
            }

            if (cnt_exact >= MAX) {
                MAX = cnt_exact;
                target = center;
            }
            if (cnt_right >= MAX) {
                MAX = cnt_right;
                target = center;
            }
            return getLongestPalidrome(s, center - 1, MAX, direction, target);
        } else {
            if (2 * (s.size() - center) < MAX ) return MAX;

            int cnt_exact = 1;                  // exactly in the center
            for (int i = 1; center + i < s.size() && center - i >= 0; i++) {
                if (s[center + i] == s[center - i]) cnt_exact += 2;
                else break;
            }

            int cnt_left = 0;                  // left
            if (center + 1 < s.size() && s[center + 1] == s[center]) {
                cnt_left = 2;
                for (int i = 1; center + 1 + i < s.size() && center - i >= 0; i++) {
                    if (s[center + 1 + i] == s[center - i]) cnt_left += 2;
                    else break;
                }
            }

            if (cnt_exact >= MAX) {
                MAX = cnt_exact;
                target = center;
            }
            if (cnt_left >= MAX) {
                MAX = cnt_left;
                target = center;
            }
            return getLongestPalidrome(s, center + 1, MAX, direction, target);
        }
    }
};

吐槽

这题明明写着 `there exists one unique longest palindromic substring,然而最后一个样例是 abcda,每一个字母都是最长子回文串,然后就 WA 了,手动再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值