0
点赞
收藏
分享

微信扫一扫

leetcode刷题记录-最长回文子串

探头的新芽 2022-04-04 阅读 82
leetcode

leetcode刷题记录

题目 最长回文子串

题目描述

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:

输入:s = “cbbd”
输出:“bb”

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母组成

思路

模板 manacher算法
Manacher算法核心
1)理解回文半径数组
2)理解所有中心的回文最右边界R,和取得R时的中心点C
3)理解 L…(i’)…C…(i)…R 的结构,以及根据i’回文长度进行的状况划分
4)每一种情况划分,都可以加速求解i回文半径的过程
什么是回文?
长度为奇数偶数,分为实轴跟虚轴两种情况
在这里插入图片描述

暴力的方法
枚举每个i位置做为中心点往左右两边扩
以i为中心的回文有多长
只能找到长度为奇数的回文串, 找不到偶数的
在这里插入图片描述

偶数的时候是不行的, cckk找不到
在这里插入图片描述

一种比较方便的处理方式, 把字符串处理一下
怎么保证长度为奇数, 跟 长度为偶数的回文串都能找到?
开头 , 结尾 ,以及每两个字符串之间都垫上一个特殊字符
长度为奇数, 偶数的回文都能找到
在这里插入图片描述

在处理串中得到的长度/2 向下取整就能知道对应的原始串长度
在这里插入图片描述

一定要求添加的字符串是源字符串没出现的吗?
无所谓 # 号 即使原字符串包含也不影响

没有任何时候, 虚的会跟实的去想比较
所以这个虚的是什么字符都行
复杂度: 最差的情况
全是一种字符, 长度2N+1
在这里插入图片描述

发现扩的时候, 每次都要扩到边界才能停
没过中心时候, 都是每次扩到左边界停止
过了中心点, 每次都是扩到右边界停
等差数列: O(N^2)
在这里插入图片描述

Manacher: O(N)
前面扩的行为会加速我后面扩的行为
前置概念

  1. 回文半径, 回文直径
    处理串不会有偶数长度的回文
    在这里插入图片描述

  2. 回文半径数组 pArr[]:
    从左往右求, 每个位置得到的答案放在pArr里 -->
    加速求i位置的答案, 利用0~i-1的信息
    在这里插入图片描述

  3. 最右回文边界
    不管你是哪个位置扩的,只要你扩出来的回文区域这个右边界变得更靠右了,
    就被我这个R变量抓住
    在这里插入图片描述

不管你是求那个回文扩的, 只要让R更往右了, R就记录这个更往右的位置
在这里插入图片描述

  1. 取得最右回文右边界的中心 C
    以0为中心扩,更新R
    C: R跟新时候的中心点
    C会跟着R更新, R如果不更新, C也不动, R跟C的含义是一对
    在这里插入图片描述

四个概念
在这里插入图片描述

算法主流程
假设现在来到i位置
以i位置为中心向左右两边扩, 分情况

  1. i 没有被R 罩住
    暴力扩, 没法优化
    在这里插入图片描述

  2. i 被R 罩住, 有优化, 有三种情况
    有图示拓扑关系, 小情况分类, 根据 i’ 自己扩出来的回文区域来分
    i’已经求过答案, 而且当初求的答案被保存在回文半径数组里
    在这里插入图片描述

  3. i’扩出的回文区域彻底在L…R内
    假设来到i位置求回文区域, i 位置扩的区域 跟 i’一样大
    在这里插入图片描述

证明
甲, 乙 关于C 对称, 甲乙逆序
i 的回文区域至少是这么大
在这里插入图片描述

假设 甲前一个字符a, 后一个字符b
乙前一个字符X, 后一个字符Y
在这里插入图片描述

  1. i’的回文区域跑到L…R的外部去了
    i…R的距离就是回文半径
    在这里插入图片描述

证明:
R’~R 一定是回文 关于C 的对称是 L~L’
在这里插入图片描述

为什么不能更长?
假设 甲前一个字符a, 后一个字符b
乙前一个字符X, 后一个字符Y
在这里插入图片描述

  1. i’ 的左边界和L 压线
    i的回文区域至少和i’一样, 会不会更大? 不知道, 需要验
    在这里插入图片描述

复杂度分析
任何一个位置失败的次数 1次, N个位置, 一共失败了N次
如果成功, 一定会推高R, R最多到N, R不回退
在这里插入图片描述

R不回退, 复杂度 O(N)
在这里插入图片描述

代码
在这里插入图片描述

半径数组中的最大值减 1 就是原始串的最大回文子串长度
在这里插入图片描述

下标变换 2c - i2∗c−i*就代表 i’
在这里插入图片描述

谁小取谁

所以这就是说我给你一个至少不用验的区域。如果你正好是这个答案,你一进来就会break,
你如果恰恰需要验,那你就继续验
在这里插入图片描述

manacher代码

public static int manacher(String s) {
		if (s == null || s.length() == 0) {
			return 0;
		}
		// "12132" -> "#1#2#1#3#2#"
		char[] str = manacherString(s);
		// 回文半径的大小
		int[] pArr = new int[str.length];
		int C = -1;
		// 讲述中:R代表最右的扩成功的位置
		// coding:最右的扩成功位置的,再下一个位置
		int R = -1;
		int max = Integer.MIN_VALUE;
		for (int i = 0; i < str.length; i++) { // 0 1 2
			// R第一个违规的位置,i>= R
			// i位置扩出来的答案,i位置扩的区域,至少是多大。
			pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;
			while (i + pArr[i] < str.length && i - pArr[i] > -1) {
				if (str[i + pArr[i]] == str[i - pArr[i]])
					pArr[i]++;
				else {
					break;
				}
			}
			if (i + pArr[i] > R) {
				R = i + pArr[i];
				C = i;
			}
			max = Math.max(max, pArr[i]);
		}
		return max - 1;
	}

	public static char[] manacherString(String str) {
		char[] charArr = str.toCharArray();
		char[] res = new char[str.length() * 2 + 1];
		int index = 0;
		for (int i = 0; i != res.length; i++) {
			res[i] = (i & 1) == 0 ? '#' : charArr[index++];
		}
		return res;
	}

Leetcode代码

public static String longestPalindrome(String s) {
        if(s == null || s.length() == 0){
            return "";
        }
        int[] manacher = manacher(s);
        if((manacher[0] & 1) == 0){
            return s.substring(manacher[1] - manacher[0] / 2 ,manacher[1] + manacher[0] / 2);
        }else {
            return s.substring(manacher[1] - manacher[0] / 2 ,manacher[1] + manacher[0] / 2 + 1);
        }

    }


    public static int[] manacher(String s){

        char[] str = manacherStr(s);
        int[] pArr = new int[str.length];
        int R = -1;
        int C = -1;
        int max = Integer.MIN_VALUE;
        int ans = 0;  //存储最大值时的位置
        for (int i = 0; i < str.length; i++) {
            pArr[i] = R > i ? Math.min(pArr[i],R - i) : 1;
            while (i - pArr[i] >= 0 && i + pArr[i] < str.length){
                if(str[i+pArr[i]] == str[i - pArr[i]]){
                    pArr[i]++;
                }else {
                    break;
                }
            }
            //注意更新R 和 C
            if(i + pArr[i] > R){
                R = i + pArr[i];
                C = i;
            }
            if(max < pArr[i]){
               max = pArr[i];
               ans = i;
            }
        }
        return new int[]{max - 1,ans / 2};
    }

    private static char[] manacherStr(String s) {
        char[] str = s.toCharArray();
        char[] res = new char[2 * str.length + 1];
        int index = 0;
        for (int i = 0; i < res.length; i++) {
            res[i] = (i & 1) == 0 ? '#' : str[index++];
        }

        return res;
    }
举报

相关推荐

0 条评论