最长回文串
两种方法
一种是使用动态规划的,这里的动态规划和最长公共子序列类似,但是计算表格的时候只要计算一般,而且是按照对角线来推进的
void subPalindrome2(char *A,char *sub)
{
int len=strlen(A);
int i=0,j=0,sublen;
int maxsublen=1;
memset(hp,0,N*N*sizeof(char));
//初始化sublen=1,2的情况
for(i=0;i<len;i++)
hp[i][i]='T';
for(i=1;i<len;i++)
{
if(A[i-1]==A[i])
{
maxsublen=2;
hp[i-1][i]='T';
}
else
hp[i-1][i]='F';
}
//规划sublen>2
for(sublen=3;sublen<=len;sublen++)
{
for(i=0,j=sublen-1;j<len;i++,j++)
{
if(A[i]==A[j])
{
hp[i][j]=hp[i+1][j-1];
if(hp[i+1][j-1]=='T')
maxsublen=sublen;
}
else
hp[i][j]='F';
}
}
for(i=0;i<len;i++)
{
for(j=0;j<len;j++)
printf("%c ",hp[i][j]);
printf("\n");
}
//找出最大字串
for(i=0,j=maxsublen-1;j<len;i++,j++)
{
if(hp[i][j]=='T')
break;
}
for(int k=i;k<=j;k++)
sub[k-i]=A[k];
}
第二种方法是Manacher算法
void Manacher(char *st,char* sub)
{
//创建临时的字符串
int len=strlen(st);
char* T=(char*)calloc(len*2+3,sizeof(char));
T[0]='@';T[2*len+2]='$';//防止首尾越界
int i,maxid=0;
for(i=1;i<=len*2;i+=2)
{
T[i]='#';
T[i+1]=st[i/2];
}
T[2*len+1]='#';
//建立回文半径数组(包括回文中心)
int* R=(int*)calloc(len*2+2,sizeof(int));
R[0]=0;
int P=0,P0=0;
for(i=1;i<=2*len+1;i++)
{
if(i<P)
R[i]=min(P-i,R[2*P0-i]-1);
else
R[i]=1;
while(T[i+R[i]]==T[i-R[i]])
R[i]++;
if(i+R[i]-1>P)
{
P=i+R[i]-1;
P0=i;
}
if(R[maxid]<R[i])
maxid=i;
}
//提取回文子串
int k;
for(k=0,i=maxid-R[maxid]+1;i<maxid+R[maxid]-1;i++)
if(T[i]!='#')
sub[k++]=T[i];
sub[k]='\0';
free(R);
free(T);
}
Manacher python实现
#coding:UTF-8
__author__ = 'LQ'
def manacher(st):
st='@'+st+'$'
T='#'.join(st)#T是转换过的数组
(P,P0,id)=(0,0,0)#P0是最远回文半径对应的中心,P是对应的最远回文半径,id是最大回文半径对应的中心,
#回文半径包含中心点,下标从1开始,下标i对应的回文串为[i-(R[i]-1),i+(R[i]-1)]
R=[0]#用来保存回文半径
for i in range(1,len(T)-1):
if i<P:
r=min(P-i,R[2*P0-i]-1)
else:
r=1
while T[i+r]==T[i-r]:r+=1
R.append(r)
(P0,P)=(i,r+i-1) if r+i-1>P else (P0,P)
id=id if R[id]>r else i
#输出回文串
sub=T[id-(R[id]-1):id+(R[id])]
sub=sub.replace('#','')
return sub
if __name__=="__main__":
print manacher('BBABCCBCAB')
最长子序列的思想和上面字符串的思想类似,,也是动态规划
//最长回文子序列
void LPS(char *st,char* sub)
{
memset(hp,0,N*N*sizeof(char));
int len=strlen(st);
int i,j;
for(i=0;i<len;i++)
{
hp[i][i]=1;
dir[i][i]='o';//表示起点
}
int sublen;
//动态规划长度大于2的字串,这里注意,对于i>j的串我们已经把hp[i][j]=0,所以计算sublen=2可以统一到下面的循环
for(sublen=2;sublen<=len;sublen++)
{
for(i=0;i<len-sublen+1;i++)
{
j=i+sublen-1;
if(st[i]==st[j])
{
dir[i][j]='+';
hp[i][j]=hp[i+1][j-1]+2;
}
else
{
if(hp[i][j-1]>hp[i+1][j])
{
hp[i][j]=hp[i][j-1];
dir[i][j]='R';
}
else
{
hp[i][j]=hp[i+1][j];
dir[i][j]='U';
}
}
//这里注意起点可能也是"aa"这样的串!!!
if(sublen==2)
dir[i][j]='o';
}
}
//for(i=0;i<len;i++)
//{
// for(j=0;j<len;j++)
// printf("%c ",dir[i][j]);
// printf("\n");
//}
//printf("%d\n",hp[0][len-1]);
//回溯出最长回文串
i=0,j=len-1;
int pbeg=0,pend=hp[0][len-1]-1;
while(dir[i][j]!='o')
{
if(dir[i][j]=='+')
{
sub[pbeg++]=sub[pend--]=st[i];
i++,j--;
}
if(dir[i][j]=='U')i++;
if(dir[i][j]=='R')j--;
}
sub[pbeg]=sub[pend]=st[i];//如果起点是一个字符pbeg=pend,如果是两个字符则不相等
}