//给你一个字符串 s,找到 s 中最长的回文子串。//////// 示例 1://////输入:s = "babad"//输出:"bab"//解释:"aba" 同样是符合题意的答案。////// 示例 2://////输入:s = "cbbd"//输出:"bb"////// 示例 3://////输入:s = "a"//输出:"a"////// 示例 4://////输入:s = "ac"//输出:"a"////////// 提示:////// 1 <= s.length <= 1000// s 仅由数字和英文字母(大写和/或小写)组成//// Related Topics 字符串 动态规划// 👍 3305 👎 0
在题解中已有动态规划和中心扩散的方法,这题主要是学习manacher算法。
在题解的视频中也有清晰的讲解,建议跟随视频把题解代码多抄几遍,跟着debug走几遍即可了解。
有这么几个特殊参数需要提前解释
maxRight: 回文子串的最大右边界,回文子串扩散到最右的位置
center: 最大右边界对应的center
i: 当前要计算回文子串的位置
mirror: 当前位置对center的镜像位置
manacher的原理就是使用动态规划的思想,计算i的时候,可以参考mirror位置的结果。
分情况讨论:
如果i>=maxRight, 使用中心扩散的方法找最大右边界
如果i<maxRight, 又分三种情况
- maxRight - i > dp[mirror]

根据dp[mirror]已经计算出来的长度,回文的区域没有超过maxRight,因此整个字符串对于center是对称的。
而且dp[mirror]已经扩散到最大,说明mirror-(dp[mirror]+1)和mirror+(dp[mirror]+1)两个字符肯定是不相同的,不然dp[mirror]就不是mirror为中心的最长回文子串了。
因此此时dp[i]的长度就可以直接取dp[mirror]
- maxRight - i == dp[mirror]

此时根据图像可以看出,mirror右边界和左边界的字母是不同的,根据maxRight的对称性,i左边界的字母和mirror右边界是相同的,maxRight+1和maxRight镜像位置-1的位置一定是不同的。
那么就有可能使i的左边界和右边界向外扩散。
因此i位置的回文串长度至少是maxRight - i,实际长度还要根据该结果向外扩展。
- maxRight - i < dp[mirror]

如果dp[mirror]的值已经超出了maxRight和center指示的回文串的范围,那么说明maxRight+1和maxRight镜像位置-1的字母一定不相同。
如果相同,maxRight的位置应该+1。
因此,根据回文串的对称性,i的左右边界一定就停在maxRight上,再无法向外扩展了。
class Solution {public String longestPalindrome(String s) {int len = s.length();// 如果字符串长度小于2, 那直接就是一个回文串if (len < 2) {return s;}// 将字符串填充, 如 'abacadv' 填充为 '#a#b#a#c#a#d#v#'String str = addDevise(s, '#');int sLen = str.length();// maxRight 标记的是回文串可以到达的最大右边界int maxRight = 0;// center是最大右边界对应的回文中点int center = 0;// 最长回文串的起始位置int begin = 0;// 最长回文串的长度int maxLength = 1;// dp存储的是i位置为中心,最长回文串的半径int[] dp = new int[sLen];char[] chars = str.toCharArray();for (int i = 0; i < sLen; i++) {// 如果i没超过回文串的最大右边界if (i < maxRight) {// mirror是i关于center对称的位置int mirror = 2 * center - i;// dp[i]至少是dp[mirror]和(maxRight和i的距离)中的最小值dp[i] = Math.min(dp[mirror], maxRight - i);}// 尝试向两边扩散int left = i - (1 + dp[i]);int right = i + (1 + dp[i]);while (left >= 0 && right < sLen && chars[left] == chars[right]) {left --;right ++;dp[i] ++;}// 维护最大右边界和centerif (i + dp[i] > maxRight) {maxRight = i + dp[i];center = i;}// 维护最长回文子串if (dp[i] > maxLength) {maxLength = dp[i];begin = (i - maxLength) / 2;}}return s.substring(begin, begin + maxLength);}public String addDevise(String s, char ch) {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(ch);for (int i = 0; i < s.length(); i++) {stringBuilder.append(s.charAt(i));stringBuilder.append(ch);}return stringBuilder.toString();}}
