📌  相关文章
📜  教资会网络 | UGC-NET CS 2017 年 11 月 – III |问题 10(1)

📅  最后修改于: 2023-12-03 15:26:05.238000             🧑  作者: Mango

UGC-NET CS 2017 年 11 月 – III | 问题 10

本题涉及到一些计算机科学中常用的数据结构和算法,需要多方面的知识储备和实践经验。

问题描述

给定一个字符串,找到其中最长的回文子串。例如,在输入字符串"babad"中,最长的回文子串是"bab"或"aba"。在输入字符串"cbbd"中,最长的回文子串是"bb"。

解题思路

暴力法

我们可以枚举所有的子串,判断其是否为回文串,并记录其中最长的。

暴力法的时间复杂度为$O(n^3)$,空间复杂度是$O(1)$。

public String longestPalindrome(String s) {
    int len = s.length();
    if (len < 2) {
        return s;
    }

    int maxLen = 1;
    int begin = 0;
    // 枚举所有长度大于等于 2 的子串
    for (int i = 0; i < len - 1; i++) {
        for (int j = i + 1; j < len; j++) {
            if (j - i + 1 > maxLen && validPalindromic(s, i, j)) {
                maxLen = j - i + 1;
                begin = i;
            }
        }
    }
    return s.substring(begin, begin + maxLen);
}

private boolean validPalindromic(String s, int left, int right) {
    while (left < right) {
        if (s.charAt(left) != s.charAt(right)) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

中心扩展算法

回文串是一个中心对称的字符串,我们可以枚举可能成为回文串中心的位置,然后从中心向两边扩展,寻找回文串。

中心扩展算法的时间复杂度为$O(n^2)$,空间复杂度为$O(1)$。

public String longestPalindrome(String s) {
    int len = s.length();
    if (len < 2) {
        return s;
    }

    int maxLen = 1;
    int begin = 0;
    // 枚举所有可能成为回文中心的位置
    for (int i = 0; i < len - 1; i++) {
        int len1 = expandAroundCenter(s, i, i);
        int len2 = expandAroundCenter(s, i, i + 1);
        int curMaxLen = Math.max(len1, len2);
        if (curMaxLen > maxLen) {
            maxLen = curMaxLen;
            begin = i - (curMaxLen - 1) / 2;
        }
    }
    return s.substring(begin, begin + maxLen);
}

private int expandAroundCenter(String s, int left, int right) {
    int len = s.length();
    while (left >= 0 && right < len && s.charAt(left) == s.charAt(right)) {
        left--;
        right++;
    }
    // 返回以left和right为中心的最长回文串的长度
    return right - left - 1;
}

Manacher算法

Manacher算法是对中心扩展算法的优化,它利用已经计算出的回文串的信息,减少了不必要的计算。

Manacher算法的时间复杂度为$O(n)$,空间复杂度为$O(n)$。

public String longestPalindrome(String s) {
    char[] chars = preprocessing(s);
    int len = chars.length;
    int[] p = new int[len];
    int center = 0; // 当前回文中心
    int mostRight = 0; // 当前回文中心最右边的坐标
    int maxLen = 0;
    int start = 0;

    for (int i = 0; i < len; i++) {
        // 初始化p[i]的值
        if (mostRight > i) {
            p[i] = Math.min(p[2 * center - i], mostRight - i);
        } else {
            p[i] = 1;
        }

        // 尝试扩展以i为中心的回文串
        while (i - p[i] >= 0 && i + p[i] < len && chars[i - p[i]] == chars[i + p[i]]) {
            p[i]++;
        }

        // 判断当前回文串是否比之前的最长回文串更长
        if (i + p[i] > mostRight) {
            center = i;
            mostRight = i + p[i];
        }

        if (p[i] > maxLen) {
            maxLen = p[i];
            start = (i - maxLen) / 2;
        }
    }
    return s.substring(start, start + maxLen - 1);
}

private char[] preprocessing(String s) {
    int len = s.length();
    char[] chars = new char[len * 2 + 1];
    int j = 0;
    for (int i = 0; i < len; i++) {
        chars[j++] = '#';
        chars[j++] = s.charAt(i);
    }
    chars[j] = '#';
    return chars;
}
总结

本题涉及到的解法主要是暴力法、中心扩展法和Manacher算法。其中,Manacher算法是时间与空间上最优的解法。需要注意,对于一些边界情况,可能需要特殊处理。