PAT甲级1040 Longest Symmetric String(JAVA版)

本题考查动态规划,可以使用Manacher(马拉车)算法,该算法能够将查找最长回文的时间复杂度降低到O(n)。

思路:很简单,就是马拉车算法,但是马拉车算法比较难理解。我自己看了很多大神的解析渐渐明白了一些东西,总结一下就是一句话:“马拉车算法的核心思想就是利用回文对称的性质,使用以前计算的每个字符的回文半径作为关于回文中心对称的字符的回文半径基础值,以便减少时间复杂度”。在以上条件不满足(即没有与其对称的字符)时,计算回文半径的方法同一般方法。”

马拉车算法解析过程请看(两篇都讲的很清楚,第一篇更详细一点):

  1. https://www.jianshu.com/p/392172762e55
  2. https://www.jianshu.com/p/116aa58b7d81

AC代码:

import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
        Scanner scaner = new Scanner(System.in);
		String str = scaner.nextLine();
		scaner.close();
        System.out.println(manacher(str));
    }

    public static char[] manacherString(String str){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            sb.append("#");
            sb.append(str.charAt(i));
        }
        sb.append("#");
        return sb.toString().toCharArray();
    }

    public static int manacher(String str){
        if(str == null || str.length() < 1)
            return 0;
        char[] charArr = manacherString(str);
        int[] radius = new int[charArr.length];
        //最右回文边界
        int R = -1;
        //最右回文中心
        int c = -1;
        //最大回文长度
        int max = Integer.MIN_VALUE;
        //遍历新回文数组
        for (int i = 0; i < radius.length; i++) {
        	/*
        	 * 若i小于最右回文边界,radius[i]初始化为Math.min(radius[2*c-i],R-i+1);在该情况下又有两种情况
        	 * 第一种情况radius[2*c-i]代表i的回文右边界小于等于最右回文边界
        	 * 第二种情况R-i+1代表i的回文右边界大于最右回文边界
        	 */
        	/*
        	 * 若i大于等于最右回文边界,radius[i]初始化为1,因为当R==i时,下一轮循环进行时,下一个字符已经跳出回文序列了所以要初始化为1
        	 */
        	//存在radius[2*c-i]大于R-i+1的情况,比如i距离字符串结尾很近时,可能会出现这种情况
            radius[i] = R > i ? Math.min(radius[2*c-i],R-i+1):1;
            if(R > i) {
            while(i+radius[i] < charArr.length && i - radius[i] > -1){
                if(charArr[i-radius[i]] == charArr[i+radius[i]])
                    radius[i]++;
                else
                    break;
            }
            if(i + radius[i] > R){
                R = i + radius[i]-1;//R代表最右回文边界的索引值,该边界包含在回文中,而不是回文的边界+1与其他的程序有区别
                c = i;
            }
            max = Math.max(max,radius[i]);
        }
        return max-1;
    }
}

马拉车算法的执行过程(为了便于理解,我自己将运行过程中部分重要参数输出到控制台便于理解):

初始字符:aabaaaaaaa
第0次的radius[0]初始值: 10次的radius[0]修改后值: 10次的R值: 00次的c值: 01次的radius[1]初始值: 11次的radius[1]修改后值: 21次的R值: 21次的c值: 12次的radius[2]初始值: 12次的radius[2]修改后值: 32次的R值: 42次的c值: 23次的radius[2*c-i]值: 23次的R-i+1值: 23次的radius[3]初始值: 23次的radius[3]修改后值: 23次的R值: 43次的c值: 34次的radius[4]初始值: 14次的radius[4]修改后值: 14次的R值: 44次的c值: 45次的radius[5]初始值: 15次的radius[5]修改后值: 65次的R值: 105次的c值: 56次的radius[2*c-i]值: 16次的R-i+1值: 56次的radius[6]初始值: 16次的radius[6]修改后值: 16次的R值: 106次的c值: 57次的radius[2*c-i]值: 27次的R-i+1值: 47次的radius[7]初始值: 27次的radius[7]修改后值: 27次的R值: 107次的c值: 58次的radius[2*c-i]值: 38次的R-i+1值: 38次的radius[8]初始值: 38次的radius[8]修改后值: 38次的R值: 108次的c值: 89次的radius[2*c-i]值: 29次的R-i+1值: 29次的radius[9]初始值: 29次的radius[9]修改后值: 49次的R值: 129次的c值: 910次的radius[2*c-i]值: 310次的R-i+1值: 310次的radius[10]初始值: 310次的radius[10]修改后值: 510次的R值: 1410次的c值: 1011次的radius[2*c-i]值: 411次的R-i+1值: 411次的radius[11]初始值: 411次的radius[11]修改后值: 611次的R值: 1611次的c值: 1112次的radius[2*c-i]值: 512次的R-i+1值: 512次的radius[12]初始值: 512次的radius[12]修改后值: 712次的R值: 1812次的c值: 1213次的radius[2*c-i]值: 613次的R-i+1值: 613次的radius[13]初始值: 613次的radius[13]修改后值: 813次的R值: 2013次的c值: 1314次的radius[2*c-i]值: 714次的R-i+1值: 714次的radius[14]初始值: 714次的radius[14]修改后值: 714次的R值: 2014次的c值: 1415次的radius[2*c-i]值: 815次的R-i+1值: 615次的radius[15]初始值: 615次的radius[15]修改后值: 615次的R值: 2015次的c值: 1516次的radius[2*c-i]值: 716次的R-i+1值: 516次的radius[16]初始值: 516次的radius[16]修改后值: 516次的R值: 2016次的c值: 1617次的radius[2*c-i]值: 617次的R-i+1值: 417次的radius[17]初始值: 417次的radius[17]修改后值: 417次的R值: 2017次的c值: 1718次的radius[2*c-i]值: 518次的R-i+1值: 318次的radius[18]初始值: 318次的radius[18]修改后值: 318次的R值: 2018次的c值: 1819次的radius[2*c-i]值: 419次的R-i+1值: 219次的radius[19]初始值: 219次的radius[19]修改后值: 219次的R值: 2019次的c值: 1920次的radius[20]初始值: 120次的radius[20]修改后值: 120次的R值: 2020次的c值: 20
最大回文长度:7

你可能感兴趣的:(JAVA,PAT)