#1032 : 最长回文子串

标签(空格分隔): hihocoder

原题目:#1032 : 最长回文子串

这道题暴力破解的复杂度是 O(n3) ,记忆搜索和动态规划时间复杂度可以降到 O(n2) ,但是空间复杂度太高。
Manacher’s Algorithm算法时间空间复杂度都是 O(n) ,leetcode有两篇博文,讲述的很是清楚。
Longest Palindromic Substring Part I
Longest Palindromic Substring Part II

中文版博客,讲解的很清晰,和英文版略有不同。Manacher’s ALGORITHM: O(n)时间求字符串的最长回文子串

珠玉在前,我就只记一记我看这个算法的一些思考:
1.为什么字符串预处理形式是“#a#b#c#”,而不可以“a#b#c”?实际代码中为何字符串首尾加入“^$”?

首先这些特殊字符的加入是为了让偶数长度和字符串与奇数长度的字符串有相同的处理方式。其次由于对称性,奇数长度的字符串在这个问题中更易处理。这两个问题都是为了处理字符串边界时更方便,保持一致性。在Machcher算法中,有一个数组P用来存放对应每个位置对应的回文字符长度,假设原字符为S,预处理后为T,P[i]的值就表示T中第i位字符对应到S中的字符并以该字符为中心点的回文子串的长度,而特殊字符对应的就是回文子串为偶数的情况。
首尾添加“^$”是为了避免数组边界越界。

2.复杂度

内循环中总次数加起来最多就N次,因此总的循环次数应该是最多2N次,因此复杂度是O(N)。关键是使用了之前的对此信息,找出了一个最小值,hihocoder也有关于这个的提示。

// 超时代码
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sin = new Scanner(System.in);

        int lines = sin.nextInt();

        while (lines-- > 0) {
            lps(sin.next());
        }

    }

    public static void lps(String input) {
        int len = 0, max = 0;

        for (int i = 0; i < input.length(); i++) {
            int len1 = maxFromCenter(input, i, i);
            int len2 = maxFromCenter(input, i, i + 1);
            len = Math.max(len1, len2);
            max = Math.max(max, len);
        }
        System.out.println(max);
    }

    public static int maxFromCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }

}

···


```java
// 学习了博客二
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sin = new Scanner(System.in);
        int lines = sin.nextInt();

        while (lines-- > 0) {
            lps_m(preProcess(sin.next()));
        }

    }

    // mancher's algorithm
    public static void lps_m(String input) {
        if (input.length() == 0) System.out.println(0);
        int[] P = new int[input.length()];
        P[0] = 0;
        int C = 0;
        int R = 0;
        int i_aside = 0;
        int max = P[0];

        for (int i = 1; i < input.length() - 1; i++) {
            i_aside = 2*C - i;

            P[i] = (R > i) ? Math.min(R - i, P[i_aside]) : 0;

            while (input.charAt(i-1-P[i]) == input.charAt(i+1+P[i])) {
                P[i]++;
            }

            if (i + P[i] > R) {
                C = i;
                R = i + P[i];
            }
            max = Math.max(max, P[i]);
        }
        System.out.println(max);
    }

    public static String preProcess(String input) {
        StringBuilder sb = new StringBuilder();
        if (input.length() == 0) return "^$";
        sb.append("^#");
        for (int i = 0; i < input.length(); i++) {
            sb.append(input.charAt(i));
            sb.append('#');
        }
        sb.append('$');
        return sb.toString();
    }
}
点赞