最长回文子串

来源:互联网 发布:mysql怎么分页 编辑:程序博客网 时间:2024/06/05 18:27

最近研究了一下最长回文子串的求法,用自己的话解释一下各个方法

需分别注意aba,abba两种回文子串的处理

1、 直接穷举法

直接穷举法显而易见,先判断每个子串是否是回文子串,然后记录长度,最后给出最长的,时间复杂度O(n^3)

2、改进穷举法

以每个字符为中心向两边扩展,得到的最长回文子串,则记录长度,否则下一个字符,依次类推,时间复杂度O(n^2),针对aba型,可以以每个字符扩展,针对abba,型,以相邻两个相同字符作为轴扩展,也可以

3、动态规划方法(并非真正的动态规划)

有母串s,用c[i,j] = 1表示子串s[i,j]为回文子串,那么就有递推式

c[i,j] = c[i+1,j-1] (  s[i] ==s[j])

 c[i,j]=0               (s[i]!=s[j])

递推式表示如果s[i+1..j-1]是回文子串,如果满足是s[i]==s[j],则s[i..j]也是回文子串;如果s[i+1..j-1]不是回文子串,则s[i..j]也不是回文子串。

初始状态 

c[i][i]=1

c[i][i+1]=1 if(s[i]==s[i+1])

上述式子表示单个字符、两个相邻的相同字符均是回文串。

核心代码如下

初始化:

for(i = 0; i < len; i++) 

{c[i][i] = 1;

if(str[i] == str[i+1])  

c[i][i+1] = 1;

}

搜索:每次从与i距离为2的字符为起点开始搜索

for(i=0;i < n;i++)

{

for(j=i+2;j<=len;j++)

{

if(str[i] == str[j])

{

c[i][j]=c[i+1][j-1];

}

else

c[i][j]=0;

if(c[i][j])

{

n = j-i+1;

if(longest < n)

{

longest = n;

}

}

}

}

4、manacher算法

求回文串时需要判断其奇偶性,也就是求aba 和abba 的算法略有差距。然而,这个算法做了一个简单的处理,很巧妙地把奇数长度回文串与偶数长度回文串统一考虑,也就是在每个相邻的字符之间插入一个分隔符,串的首尾也要加,当然这个分隔符不能再原串中出现,一般可以用‘#’或者‘$’等字符。例如:
原串:abaab
新串:#a#b#a#a#b#
这样一来,原来的奇数长度回文串还是奇数长度,偶数长度的也变成以‘#’为中心奇数回文串了。

然后算法的核心思想如下:

用一个辅助数组P 记录以每个字符为中心的最长回文半径,也就是P[i]记录以Str[i]字符为中心的最长回文串半径。P[i]最小为1,此时回文串为Str[i]本身。
我们可以对上述例子写出其P 数组,如下
新串: # a # b # a # a # b #
P[] : 1 2 1 4 1 2 5 2 1 2 1
我们可以证明P[i]-1 就是以Str[i]为中心的回文串在原串当中的长度。

依次从前往后求得P 数组就可以了,求P[i] 的时候,前面的P[]值已经得到了,我们利用回文串的特殊性质可以进行一个大大的优化。

为了防止求P[i]向两边扩展时可能数组越界,我们需要在数组最前面和最后面加一个特殊字符,令P[0]=‘$’最后位置默认为‘\0’不需要特殊处理。


最关键的优化:


我们用MaxId 变量记录在求i 之前的回文串中,延伸至最右端的位置,同时用id 记录取这个MaxId 的id 值。如果i还没有到达Maxid,即Maxid>i,则可以利用回文串的对称性根据之前计算的P[]来计算P[i]

如果已经超过了Maxid 即 i<= Maxid,则P[i]只能从1开始计算,不能做任何优化。

下面讨论Maxid > i 的情况,首先明确Maxid和id表示的含义分别代表了i之前的回文串中延伸最右的位置及其对应的中心点,则j=2*id - i,i关于id的对称点,这个点的值之前已经求过了,即p[j];  考虑p[j]的大小,如果p[j]<max-i,即以j为中心的最长回文串半径没有超出maxid,也即以j为中心的最长回文串就在以id为中心的最长回文串半径之内,由于对称性,这部分对称到i为中心仍然为回文串,然后以此为基础,判断是否有更长的串为回文串,如果p[j]>maxid-i,则以id为中心的最长回文串没有完全包含以j为中心的最长回文串,没有超出部分i~maxid(忽略了另一半)肯定是回文串,超出部分要重新比较,判断是否是回文串,进而计算p[i].

这个过程写成代码为

if(maxid > i)

 p[i] = min(p[2*id - i],maxid - i);

else

p[i]=1;

继续判断:

while(str[i+p[i]]==str[i-p[i]])p[i]++;

即可得到新的p[i];
记录下新的Maxid,及id:

if(p[i]+i > maxid)

{

maxid = p[i]+i;

id = i;
}

记录最长的回文子串

if(longest <p[i])

{longest = p[i];

max_i = i; //记录下中心值,便于输出查找

}

算法复杂度,由于利用了p[j]及maxid,第二重循环的比较次数大大减少了,算法的时间复杂度近似与O(n);

0 0
原创粉丝点击