2019.4.14 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)
Palindromic Substring, 最长回文子串问题。
“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
主要有两种方法:
1.中心拓展法
回文子串从中心展开,可能奇偶有两种情况。这样的回文中心一共可能有2n−1个这样的中心。从中心元素开始依次检查对称关系是否成立。每当回文子串长度超过当前最大长度时停止更新。
2.Manacher算法
Manacher算法说明参考链接1
Manacher算法说明参考链接2
Manacher算法的几个定义:
1.Manacher数组T
为了将奇偶回文中心等同考虑,可以在每一个字符两侧加上一个符号“#”,构建成一个特定的Manacher数组。比如输入的字符为acbbcbds,用“#”字符处理之后的新字符串就是#a#c#b#b#c#b#d#s#。
2.回文半径数组radius
回文半径数组radius是用来记录以每个位置的字符为回文中心求出的回文半径长度,包括每一个#。例如#a#c#b#b#c#b#d#s#的回文半径数组为[1, 2, 1, 2, 1, 2, 5, 2, 1, 4, 1, 2, 1, 2, 1, 2, 1]。
3.对称中心C与最右回文边界R
一个位置最右回文右边界指的是这个位置及之前的位置的回文子串,所到达的最右边的地方。例如Manacher数组#a#b#a#b#c#b#d#s#,当以idx=3的第一个b为中心时,R=6,最右回文边界R落在idx=6的"#"上。当以idx=4的第一个b为中心时,最右回文边界没有更新,仍然为6。
4.当前位置p,p以C为中心的对称镜像p’,以C为中心的回文左边界cL,以p’为中心的回文左边界pL。
Manacher算法的流程:
首先从左往右依次计算radius[p],当计算radius[p]时,radius[p’](0 <= j < i)已经计算完毕。分两种情况:
p <= R
如果radius[p’] < R-p,说明以p’为中心的回文串一定在以C为中心的回文串的内部,且p’和p关于位置C对称,由回文串的定义可知,一个回文串反过来还是一个回文串,所以以p为中心的回文串的长度至少和以p’为中心的回文串一样,即radius[p] >= radius[p’]。因为radius[p’] < R - p,所以说p + radius[p’] < R。由对称性可知radius[p] = Len[p’]。
如果radius[p’] >= R-p,由对称性,说明以p为中心的回文串可能会延伸到R之外,而大于R的部分我们还没有进行匹配,所以要从R+1位置开始一个一个进行匹配,直到发生失配,从而更新R和对应的C以及radius[p]。
p > R
如果p比R还要大,说明对于中点为p的回文串还一点都没有匹配,这个时候,就只能老老实实地一个一个匹配了,匹配完成后要更新R的位置和对应的C以及radius[i]。
传送门:最长回文子串
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
/**
*
* @author ChopinXBP
* Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
* Input: "babad" Output: "bab" Note: "aba" is also a valid answer.
* 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
* 输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
*
*/
public class LongestPalindromicSubstring {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(longestPalindrome("cabad"));
System.out.println(longestPalindrome("accbcd"));
System.out.println(longestPalindrome("eababag"));
System.out.println(longestPalindrome("abcdag"));
System.out.println(longestPalindrome("babadada"));
}
//////////////////////中心拓展法//////////////////////
//中心拓展法1:时间复杂度:O(n^2),空间复杂度:O(1)
//回文中心的两侧互为镜像,因此可以从它的中心展开,并且只有2n−1个这样的中心(分对称轴为单个元素和对称轴为两个元素两种情况)。
public static int left, maxlen;
public static String longestPalindrome(String s) {
if(s == null || s.length() < 2)return s;
left = 0;
maxlen = 1;
//分对称轴为单个元素和对称轴为两个元素两种情况
for(int i = 0; i < s.length(); i++) {
findMaxPalindrome(s, i, i);
findMaxPalindrome(s, i, i + 1);
}
return s.substring(left, left + maxlen);
}
public static void findMaxPalindrome(String s, int latter, int former) {
//从中心元素开始依次检查对称关系是否成立
while(latter >= 0 && former < s.length() && s.charAt(latter) == s.charAt(former)) {
latter--;
former++;
}
//回文子串长度超过当前最大长度,则更新起止坐标
if(former - latter - 1 > maxlen) {
left = latter + 1;
maxlen = former - latter - 1;
}
}
//中心拓展法2(无全局变量)
public static String longestPalindrome1(String s) {
if (s == null || s.length() < 1)
return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private static int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}
////////////////////////Manacher算法/////////////////////////
//https://www.jianshu.com/p/116aa58b7d81
//字符串转换:包含头尾的所有字符间隔插入‘#’
public static char[] manacherString(String str) {
StringBuilder newstr = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
newstr.append("#");
newstr.append(str.charAt(i));
}
newstr.append("#");
return newstr.toString().toCharArray();
}
public static int longestPalindrome2(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 p = 0; p < radius.length; p++) {
int p1 = 2 * C - p;
radius[p] = R > p ? Math.min(radius[p1], R - p + 1) : 1;
while (p + radius[p] < charArr.length && p - radius[p] > -1) {
if (charArr[p - radius[p]] == charArr[p + radius[p]]) {
radius[p]++;
} else {
break;
}
}
if (p + radius[p] > R) {
R = p + radius[p] - 1;
C = p;
}
max = Math.max(max, radius[p]);
}
return max - 1;
}
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#