问题描述:
对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。给定字符串A以及它的长度n,请返回最长回文子串的长度。
测试样例:
"abc1234321ab",12
返回:7
暴力法: 使用两层循环找到所有子串,第三层循环判断子串是否是回文。方法的时间复杂度为O(n^3),空间复杂度为O(1)。
动态规划:开辟一个f(i,j)表示s[i..j]的最长回文子序列,状态转移方程如下:
当i>j时,f(i,j)=0。
当i=j时,f(i,j)=1。
当i<j并且s[i]=s[j]时,f(i,j)=f(i+1,j-1)+2。
当i<j并且s[i]≠s[j]时,f(i,j)=max( f(i,j-1), f(i+1,j) )。
这个方法的时间复杂度为O(n^2),空间复杂度为O(n^2),比暴力法有很大的改进。
中心扩展:对于每一个回文子串可以先确定一个中心,然后向两边扩展,这样可以在时间复杂度O(n^2),空间复杂度O(1) 的情况下完成,需要注意的是,长度为奇数和偶数的中心的情况是不同的。
Manacher:是一种线性时间的方法,非常巧妙。首先,我们在上面的方法都要考虑回文长度为奇数或者偶数的情况。这个 方法引入一个技巧,使得奇数和偶数的情况统一处理了。具体做法参考:https://www.felix021.com/blog/read.php?2040
使用 Manacher方法实现的AC代码:
class Palindrome {
public:
int getLongestPalindrome(string A, int n) {
// write code here
string str="#";
if(n==0)
return 0;
if(n==1)
return 1;
for(int i=0;i<n;i++)
str=str+A[i]+"#";
int len=str.size();
int *pArr = new int[len];
int pR=0,index=-1;
int tempi,tempil,indexl;
for(int i=0;i<len;i++){
if(i<pR){
tempi=2*index-i;
tempil=tempi-pArr[tempi];
indexl=index-pArr[index];
if(tempil!=indexl)
pArr[i]=min(pArr[tempi],tempi-indexl);
else {
pArr[i]=pR-i;
for(int j=pR-i;i-j>=0 && i+j<len;j++){
if(str[i-j]!=str[i+j])
break;
pArr[i]++;
}
if(i+pArr[i]>pR){
index=i;
pR=i+pArr[i];
}
}
}
else {
pArr[i]=1;
for(int j=1;i-j>=0 && i+j<len;j++){
if(str[i-j]!=str[i+j])
break;
pArr[i]++;
}
if(i+pArr[i]>pR){
index=i;
pR=i+pArr[i];
}
}
}
int max=0;
for(int j=0;j<len;j++)
if(max<pArr[j])
max=pArr[j];
return max-1;
}
};