1.暴力法
找出所有子串,并且判断是否是回文子串,并且记录最长长度,算法复杂度为O(n^3),强烈不推荐。
2.对称求解法
遍历字符串每个位置,从每个位置向两边扩散,判断是否对称,算法复杂度为O(n^2),但是需要考虑长度为奇偶两种情况。
示例代码:
public int longestPalindrome(String str) {
int length=str.length();
if(str==null||length<1) {
return 0;
}
int result=0;
int max=0;
//遍历所有位置
for(int i=0;i<length;i++) {
//考虑str长度为奇数的情况,j为两边移动的距离
for(int j=0;j<=i&&i+j<length;j++) {
if(str.charAt(i-j)!=str.charAt(i+j)) {
break;
}
max=j*2+1;
}
if(max>result) {
result=max;
}
//考虑str长度为偶数的情况,j为移动的距离
for(int j=0;j<=i&&i+j+1<length;j++) {
if(str.charAt(i-j)!=str.charAt(i+j+1)) {
break;
}
max=j*2+2;
}
if(max>result)
result=max;
}
return result;
}
3.Manacher算法
从解法2我们可以发现仍有很多子串被重复判断,所以就引入了Manacher算法。
第一个不同:首先Manacher算法为了解决奇偶问题在原字符串中插入了#符号,将字符串长度统一为奇数,例如将字符串abcbae扩充为#a#b#c#b#a#e#
第二个不同:Manacher算法引入了一个回文半径,即用一个数组保存每个字符的回文半径,回文半径初始化为1,(本身),求解回文半径方法见代码注释。
public int longestPalindrome(String str) {
//扩充字符串
StringBuilder str2 = new StringBuilder();
str2.append('#');
for (int i = 0; i < str.length(); i ++) {
str2.append(str.charAt(i));
str2.append('#');
}
//回文半径数组
int [] rad = new int[str2.length()];
// right表示已知的最长回文子串最右边缘坐标
int right = -1;
// center表示已知的最长回文子串中点坐标
int center = -1;
for (int i = 0; i < str2.length(); i ++) {
//初始回文半径1,自己本身
int r = 1;
// 找到i的关于center对称位置j,j=2*center-i
//如果对称位置的rad小于right-i,说明以j为中心的回文子串在已知最长回文子串中,由对称性可知,rad[i]=rad[j]
//否则i的回文半径就是right-i,所以回文半径就是right-i,所以就相当于取两个的最小值,这种情况就需要再向两边进行匹配
//进行这个判断的原因是为了省去重复字符串扫描
if (i <= right) {
r = Math.min(right-i, rad[2 * center - i]);
}
// 向两边进行匹配
while (i - r >= 0 && i + r < str2.length() && str2.charAt(i - r) == str2.charAt(i + r)) {
r++;
}
// 如果扫描结束后位置大于right,需要更新right和center
if (i + r - 1> right) {
right = i + r - 1;
center = i;
}
rad[i] = r;
}
// 扫描最大长度
int result = 0;
for (int r : rad) {
if (r > result) {
result = r;
}
}
return result - 1;
}