public class Solution {
public int getLongestPalindrome (String str) {
// 添加特殊字符,使得str为奇数长度的字符串,如aabb => #a#a#b#b#
StringBuilder newStr = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
newStr.append('#');
newStr.append(str.charAt(i));
}
newStr.append('#');
str = new String(newStr);
// dp数组,dp[i]用于记录以str[i]为中心的最大回文半径
int[] dp = new int[str.length()];
dp[0] = 1;
// 维护一个区间[L, R],M是区间中点,该区间代表目前已知最靠右的回文串,dp与[L, R]互相帮助
int L = 0, R = 0, M = 0;
// 递推
for (int i = 1; i < str.length(); i++) {
// 当前位置落在[L, R]之外,最长回文串先认为是自身str[i]
if (i > R) {
dp[i] = 1;
}
// 落在[L, R]之内
else {
// 计算i关于M的对称点k,利用(i + k)/2 = M
// 需注意对称的含义:在[L, R]的范围内,str[..k..]与str[..i..]完全一样
int k = 2 * M - i;
// k为中心的最长回文串不超过L,则dp[k]就是dp[i],否则只利用k不超出L的部分
dp[i] = Math.min(dp[k], k - L + 1);
}
// 除了dp[i] = dp[k]的情况,其余的都是未探索完的情况,故而需进行探索
// dp[i] = dp[k]虽然可以不用探索,但为了好写代码,一起进行探索,它探索一个字符就会自动停止
while (i - dp[i] >= 0 && i + dp[i] < dp.length && str.charAt(i - dp[i]) == str.charAt(i + dp[i])) {
dp[i]++;
}
// 更新区间[L, R]
if (i + dp[i] - 1 > R) {
L = i - dp[i] + 1;
M = i;
R = i + dp[i] - 1;
}
}
// 搜索结果,由于填充了#,新的str的最大回文半径其实是旧的直径加1
int res = 0;
for (int i = 0; i < dp.length; i++) {
if (dp[i] - 1 > res) {
res = dp[i] - 1;
}
}
return res;
}
}