其实求最长回文子串就是求最长对称子串
【法一】
最容易想到的就是找出所有的子串(O(n^2)),判断每个字符是否对称(O(n)),所以总的时间复杂度是O(n^3)。
【法二】法一是“从外向里”判断,相反,可以“从里向外”判断,时间复杂度O(n^2),代码如下
void GetLongestSymmetircalSubstring(char *pStr)
{
int symmetricalLen=1;
char *pChar=pStr;
char maxSubsring[100];
memset(maxSubsring,0,100);
maxSubsring[0]=*pStr;
while(*pChar!='\0')
{
char *pFirst=pChar-1;
char *pLast=pChar+1;
//处理奇数长度的子串
while(pFirst>=pStr && *pLast!='\0' && *pFirst==*pLast)
{
pFirst--;
pLast++;
}
int newLen=pLast-pFirst-1;
if (newLen>symmetricalLen)
{
symmetricalLen=newLen;
strCpy(maxSubsring,pFirst+1,pLast-1);
}
//处理偶数长度的子串
pFirst=pChar;
pLast=pChar+1;
while (pFirst>=pStr && *pLast!='\0' && *pFirst==*pLast)
{
pFirst--;
pLast++;
}
newLen=pLast-pFirst-1;
if (newLen>symmetricalLen)
{
symmetricalLen=newLen;
strCpy(maxSubsring,pFirst+1,pLast-1);
}
pChar++;
}
cout<<"字符串pStr的最长对称子串长度是:"<<symmetricalLen<<"\t"<<"子串为:"<<endl;
cout<<maxSubsring<<endl;
}
void main()
{
char str[100]="google";
GetLongestSymmetircalSubstring(str);
}
注:法一及法二参见何海涛老师的博客http://zhedahht.blog.163.com/blog/static/25411174201063105120425/
【法三】O(n)求最长回文子串---Manacher算法
本法的优点不仅是O(n)复杂度,而且不用考虑奇数子串和偶数子串的情况。
Manacher算法详细解析请看文末的参考文章,以下代码是我自己试着写的。
int Min(int& a,int& b)
{
if(a<=1||b<=1)
return 1;
if(a<b)
return a;
return b;
}
void GetLongestPalindromicSubstring(char *s)
{
char *pCh=s;
int len=0; //原字符串pStr的长度
while (*pCh++!='\0')
len++;
int newLen=2*len+2;//扩展后字符串长度
char *str=new char[newLen+1];//分配多分配一个内存,用于存储'\0'
str[0]='$';
str[1]='#';
str[newLen]='\0';//以'\0'结尾
int i;
for (i=0;i<len;i++)//构建新字符串
{
str[2*i+2]=s[i];
str[2*i+3]='#';
}
cout<<"原来字符串:";//输出原字符串,做参考用,可有可无
for (i=0;i<len;i++)
{
cout<<s[i]<<" ";//字符间加空格,方便观看
}
cout<<endl;
i=0;
cout<<"扩展字符串:";
while(str[i]!='\0')//这个循环可有可无,输出扩展的字符串仅做参考用,字符间加了个空格
cout<<str[i++]<<" ";
cout<<endl;
int *p=new int[newLen];//构造数组p[],p[i]为扩张数组中i位置的最长回文串半径
memset(p,1,newLen*sizeof(int));//初始化为1,即每一个p[i]的回文串初始为1
p[0]=0;//扩展串str第一个字符为'$'
int id=0;
int max=0;
for(i=1;i<newLen;i++)//构造数组p的过程,也是本算法的核心所在,详见文末参考文章
{
if (max>i)
{
int diff=max-i;//用变量diff代替max-i纯是为了调用Min函数,因为max-i为常量值,不能调用Min
p[i]=Min(p[2*id-i],diff);
}
else
p[i]=1;
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
if (p[i]+i>max)
{
max=p[i]+i;
id=i;
}
}
cout<<"下标 数 组:";
for (i=0;i<newLen;i++)//输出数组p,仅做参考用
{
cout<<p[i]<<" ";
}
cout<<endl;
int rad=0;
int j,index=0;
for (i=1;i<newLen;i++)
{
if(p[i]-1>rad)
{
rad=p[i]-1;//p[i]中的最大值-1即为原字符串中的最大回文串长度rad
index=i;//index记录具有最大回文串长度的扩展串中的字符的下标
}
}
cout<<"最大回文子串长度为:"<<rad<<endl;
//以下相当于由扩展字符串下标映射回原字符串的下标
if (str[index]=='#')//如果具有最长回文串的字符是'#',作如下处理,画图更容易理解
{
i=(index-3)/2;
for (j=i-(rad/2-1);j<=i+rad/2;j++)
{
cout<<s[j];
}
cout<<endl;
}
else //如果具有最长回文串的字符是原字符串中的字符,作如下处理
{
i=(index-2)/2;
for (j=i-rad/2;j<=i+rad/2;j++)
{
cout<<s[j];
}
cout<<endl;
}
}
void main()
{
char s[]="waabwswfd";
GetLongestPalindromicSubstring(s);
}
测试1:字符串"waabwswfd"
测试2:字符串"google"
测试3:字符串"cdfacbghgbcadba"
Manacher算法比较好的参考文章:
1、 O(n) 求 最长回文子串 http://www.cnblogs.com/wuyiqi/archive/2012/06/25/2561063.html
2、Manacher算法 http://blog.youkuaiyun.com/adrastos/article/details/9093779#