回文字符串是笔试中常考的题,下面给出回文字符串的一个经典题型:
1.最基本的解法
对于这个问题,我们考虑到回文字符串的性质,即回文字符串正读,反读相同,左右对称,这样一来,最基本的方法就是得到所求字符串的所有子串,并判断它们是否是左右对称,方法如下:
bool isAym(char *cbegin, char *cend)
{
if(cbegin == NULL || cend ==NULL || cbegin > cend)
{
return false;
}
while(cbegin<cend)
{
if(*cbegin!=*cend)
{
return false;
}
cbegin++;
cend--;
}
return true;
}
int getMaxSym(char * str)
{
if(str == NULL)
return 0;
int maxlength = 0, strlength = 0;
char *pFirst = str;
char *strEnd = str + strlen(str);
while(pFirst < strEnd)
{
char *pLast = strEnd;
while(pLast > pFirst)
{
if(isAym(pFirst, pLast))
{
strlength = pLast - pFirst + 1;
if(strlength > maxlength)
{
maxlength = strlength;
}
}
pLast --;
}
pFirst ++;
}
return maxlength;
}
既然是最基本的方法,那么不可避免的就是他的效率问题,时间复杂度为O(n^3),显然效率不太高
2.改进方法
对于上面的那种方法,之所以会效率低,很重要的一点就是它对字符串的判断是从外向里的,即先判断"abba",在判断其中的"bb",而当范围较大的字符串进行过的判断,在范围小的字符串中又进行了判断,这样一来就进行了大量重复的判断
而可以采取的改进措施就是将对字符串的判断改成从里向外,也就是先判断子字符串(如dd)是不是对称的。如果它(dd)不是对称的,那么向该子字符串两端各延长一个字符得到的字符串肯定不是对称的。如果它(dd)对称,那么只需要判断它(dd)两端延长的一个字符是不是相等的,如果相等,则延长后的字符串是对称的。
int getMaxSym2(char * str)
{
if(str == NULL)
return 0;
int maxlength = 0;
char *ptag = str;
while(*ptag !='\0')
{
//奇数子字符串
char *left = ptag - 1;
char *right = ptag + 1;
int oddlenght = 1;
while(left >= str && *right != '\0' && *left == *right)
{
left--;
right++;
oddlenght += 2;
}
if(oddlenght > maxlength)
{
maxlength = oddlenght;
}
//偶数子字符串
left = ptag;
right = ptag + 1;
int evenlength = 0;
while(left >= str && *right != '\0' && *left == *right)
{
left--;
right++;
evenlength += 2;
}
if(evenlength > maxlength)
{
maxlength = evenlength;
}
ptag++;
}
return maxlength;
}
由于子字符串的长度可能是奇数也可能是偶数。长度是奇数的字符串是从只有一个字符的中心向两端延长出来,而长度为偶数的字符串是从一个有两个字符的中心向两端延长出来。因此程序中要把这两种情况都考虑进去。
3.manacher算法
这里我们利用一个辅助数组,用来记录以字符串中的每个字符为中心的最长回文字符子串长度,当然为了避免上面的方法中遇到的j奇偶字符串的问题带来的麻烦,同时也为了避免在算法中频繁考虑数组的越界问题,我们对原字符串进行变形(即原字符串为"abaab",这里我们将其变为"$#a#b#a#a#b#")
方法如下:
char* GetNewStr(char* str)
{
char* dst=new char[2*strlen(str)+3];
int i=0;
dst[i++]='$';
while(*str)
{
if(i%2==0)
{
dst[i++]=*str;
str++;
}
else
{
dst[i++]='#';
}
}
dst[i++]='#';
dst[i]='\0';
return dst;
}
int GetMaxSubBackStr(char* str)
{
if(str==NULL)
return 0;
char* dst=GetNewStr(str);
int* len=new int[strlen(dst)];
len[0]=0;
int i=1;
int maxlen=0;
int idx=0;
for(;i<strlen(dst);i++)
{
if(i<maxlen)
{
len[i]=len[2*idx-i]>maxlen-i?maxlen-i:len[2*idx-i];
}
else
{
len[i]=1;
maxlen=len[i]+i;
idx=i;
}
while(i-len[i]>=0&&i+len[i]<strlen(dst)&&dst[i-len[i]]==dst[i+len[i]])
{
len[i]++;
}
}
maxlen=0;
for(i=0;i<strlen(dst);i++)
{
if(maxlen<len[i])
maxlen=len[i];
}
return maxlen-1;
}
这里进行了一些优化:当我们求某一个字符(位置为i)的对应最长回文字符串长度的时候,可以考虑它是否在前面求过的某个字符(位置为idx)对应的最长回文字符串当中,若在这当中,则可以利用这个条件,求出关于idx与i对称的位置j,看其边界是否超过idx对应的边界,若超过,则len[i]的值为maxlen-i,否则为对称位置的len[j]
参考链接:http://blog.csdn.net/yzl_rex/article/details/7908259
http://www.cnblogs.com/houkai/p/3371807.html