leetcode 5. 最长回文子串
递推公式
边界条件: dp[0] = 0
已知dp[i], 可以快速向后推倒: dp[i] -> dp(i, i+dp[i])
代码
static
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public String longestPalindrome(String s) {
if (s.length() < 2) {
return s;
}
// 动态规划, 创建Manacher算法的dp数组
ManacherDp manacherDp = ManacherDp.create(s);
// 有了dp数组后, 计算最大回文子串
return manacherDp.getLongestPalindrome();
}
/**
* 往字符串任意两个相邻字符中间插入一个字符, 形成基数长度新的字符串
* 封装对这个字符串的操作
*
* @author zhengyongpan
* @since 2021/12/10 15:02
*/
public static class NormalizedString {
private String raw;
private char seperator;
private int length;
/**
* 已知NormalizedString中字符的位置, 计算原始字符串中字符的位置
*
* @param pos pos
* @return int
* @author zhengyongpan
* @since 2021/12/10 15:27
*/
public static int unnormalizePos(int pos) {
return pos >> 1;
}
/**
* 判断指定位置是不是特殊字符
*
* @param pos pos
* @return boolean
* @author zhengyongpan
* @since 2021/12/10 15:27
*/
public static boolean isSpecial(int pos) {
return (pos & 1) == 1;
}
/**
* 根据NormalizedString中的回文子串, 求原始字符串中最长回文子串的长度
*
* @param normalizePos normalizePos
* @param normalizeOffset normalizeOffset
* @return int
* @author zhengyongpan
* @since 2021/12/10 15:09
*/
public static int calcUnnormalizeLength(int normalizePos, int normalizeOffset) {
if ((normalizePos & 1) == 0) {
// 在字符上
return 1 + ((normalizeOffset >> 1) << 1);
} else {
// 在间隙上
return ((normalizeOffset + 1) >> 1) << 1;
}
}
/**
* @param centerPos centerPos NormalizedString字符串中, 回文字符串中心字符索引
* @param offset offset NormalizedString字符串中, 回文字符串臂长
* @return int[] 原始字符串中, 回文字符串的起止位置
* @author zhengyongpan
* @since 2021/12/10 15:17
*/
public static int[] toLongestPalindromePos(int centerPos, int offset) {
// 将NormalizedString中的最大回文子串, 转换为原始字符串中的最大回文子串
int leftPos;
int rightPos;
if (NormalizedString.isSpecial(centerPos)) {
// 在间隙上
leftPos = NormalizedString.unnormalizePos(centerPos - offset + 1);
rightPos = NormalizedString.unnormalizePos(centerPos + offset);
} else {
// 在字符上
leftPos = NormalizedString.unnormalizePos(centerPos) - (offset >> 1);
rightPos = NormalizedString.unnormalizePos(centerPos) + (offset >> 1);
}
return new int[]{leftPos, rightPos + 1};
}
public NormalizedString(String str, char seperator) {
this.raw = str;
this.seperator = seperator;
this.length = (str.length() << 1) - 1;
}
public int length() {
return (raw.length() << 1) - 1;
}
public char charAt(int index) {
if ((index < 0) || (index >= length)) {
throw new StringIndexOutOfBoundsException(index);
}
if ((index & 1) == 1) {
return seperator;
}
return raw.charAt(index >> 1);
}
/**
* 已知str[centerPos-offset, centerPos+offset]是回文串
* 求以centerPos为中心的最长回文子串的臂长
*
* @param str str
* @param centerPos centerPos
* @param offset offset
* @return int
* @author zhengyongpan
* @since 2021/12/10 11:15
*/
private int expand(int centerPos, int offset) {
offset++;
int left = centerPos - offset;
int right = centerPos + offset;
while (left >= 0 && right < length && charAt(left) == charAt(right)) {
left--;
right++;
}
return ((right - left) >> 1) - 1;
}
/**
* 创建dp数组
* dp[i] 表示 NormalizedString以位置i为中心的最大回文子串的臂长
*
* @param
* @return int[]
* @author zhengyongpan
* @since 2021/12/10 15:32
*/
public int[] createDp() {
int len = length();
int[] dp = new int[len];
int i = 1;
while (i < len) {
int maxOffset = expand(i, 0);
dp[i] = maxOffset;
if (maxOffset < 2) {
i++;
} else {
for (int offset = 1; offset < maxOffset; offset++) {
int centerPos = i + offset;
dp[centerPos] = expand(centerPos, Math.min(dp[i - offset], maxOffset - offset));
}
i += maxOffset;
}
}
return dp;
}
public String getRaw() {
return raw;
}
}
public static class ManacherDp {
private NormalizedString str;
private int[] dp;
private ManacherDp() {
}
/**
* 创建 ManacherDp
*
* @param strRaw strRaw
* @return leetcode.editor.cn.Q5_LongestPalindromicSubstring.Solution.ManacherDp
* @author zhengyongpan
* @since 2021/12/10 15:21
*/
public static ManacherDp create(String strRaw) {
ManacherDp manacherDp = new ManacherDp();
// 归一化字符串, 用特殊字符填充空隙, 得到新的长度为基数的字符串
NormalizedString str = new NormalizedString(strRaw, '#');
manacherDp.str = str;
// 用manacher算法进行动态规划, 计算dp数组
manacherDp.dp = str.createDp();
return manacherDp;
}
/**
* 返回原始字符串中的最大回文子串
*
* @param
* @return java.lang.String
* @author zhengyongpan
* @since 2021/12/10 15:21
*/
public String getLongestPalindrome() {
// 计算原始字符串中最大回文子串的位置
int[] longestPalindromePos = getLongestPalindromePos();
// 截取最大回文子串
return str.getRaw().substring(longestPalindromePos[0], longestPalindromePos[1]);
}
/**
* 计算原始字符串中, 最长的回文子串的位置
*
* @param dp dp
* @return int[]
* @author zhengyongpan
* @since 2021/12/10 15:08
*/
private int[] getLongestPalindromePos() {
// 计算NormalizedString中最大回文字串的位置
int[] longestPalindromeInNormalizedString = getLongestPalindromeInNormalizedString();
// 将NormalizedString中回文子串的位置, 转换为原始字符串中回文子串的位置
return NormalizedString.toLongestPalindromePos(longestPalindromeInNormalizedString[0], longestPalindromeInNormalizedString[1]);
}
/**
* 计算NormalizedString中的最大回文子串
* 以中心索引和臂长表示
*
* @param
* @return int[] {中心索引, 臂长}
* @author zhengyongpan
* @since 2021/12/10 15:20
*/
private int[] getLongestPalindromeInNormalizedString() {
int len = dp.length;
// 在NormalizedString中寻找最大回文子串
int maxOffset = 0;
int maxPos = 0;
int maxLength = 1;
for (int pos = 1; pos < len; pos++) {
// 计算原始字符串中回文子串的长度
int unnormalizeLength = NormalizedString.calcUnnormalizeLength(pos, dp[pos]);
if (unnormalizeLength > maxLength) {
maxOffset = dp[pos];
maxPos = pos;
maxLength = unnormalizeLength;
}
}
return new int[]{maxPos, maxOffset};
}
}
}
//leetcode submit region end(Prohibit modification and deletion)