【面试题】最长回文子串
这个也算是很经典的题目了,O(n)的解法还是要自己钻进去想一想的,不能总以为自己会了,动手写一写才是王道。
今天看了2013年9月15日北理工面试&算法讲座by_July&曹鹏的PPT,真是后悔当时没去听啊,已经不记得当时干什么去了,反正应该没有去听讲座有意义。。正题,然后又看到这个题,第N次看到了,却还没写过,实在不应该,今天就写了一下。话说,推荐 结构之法 算法之道 博客,博客置顶的博文里面可以找到PPT下载地址。
题大家应该都知道,我就直接用PPT里面的了。
例如ababcdedcbaab, 最长回文子串是abcdedcba。
暴力解法1:
枚举起点O(n)
枚举终点O(n)
判断回文O(n)
总复杂度O(n^3)
暴力解法2:
枚举中间点O(n) (可能是两个字符之间,可能是1个字符)
不断延伸判断回文O(n)
总复杂度O(n^2)
O(n)算法: Manacher
奇数偶数统一处理,每个字符前后一个字符串中没出现的字符
例如aba,变为了#a#b#a#,这样做的好处奇数、偶数统一处理。
定义数组p[i],表示以i为中心[i – x ... i + x]是最长且回文的。
还是上例:
#a#b#a#
0103010
P[i]其实表示了原串中以该字符为中心的最长回文子串的长度(#表示原串中两个字符之间的位置为中心)。
我们能否O(n)时间求出p数组?
Manacher算法
算p[i]时假设p[0 ... i – 1]已经计算好了,对前面的p[x],我们定义一个框[x – p[x] ... x + p[x]],定义right是max{x + p[x]}, center = argmax{x + p[x]},即right是之前所有框的最右边的位置,center是这个框的中心,框里的串是个回文子串。
现在要计算p[i],我们算一下其关于center的对称点i’ = center * 2 – i,问题关键是回文串的对称性,从框左边left...i’和i...right是一致的,如果p[i’]的长度能把i + p[i’]限制在框里,则有p[i] = p[i’],因为框住的东西比较过了。
如果框不住p[i’]的长度,导致其超出i + p[i’]右边界,就是框不住的东西,是需要比较的。
p[i]至少等于min{p[i’], right – i}。
继续暴力比较,更新right。
为啥算法是O(n)的?因为right只能增加n次……
code:
1 int MaxHuiwen(string str) 2 { 3 if(str.empty()) 4 return 0; 5 string strCharp = "#"; 6 for(int i = 0; i < str.length(); ++i) 7 { 8 strCharp += str[i]; 9 strCharp += "#"; 10 } 11 int len = strCharp.length(); 12 int *p = new int[len]; 13 p[0] = 0; 14 int l = 0, c = 0, r = 0, maxl = 1; 15 for(int i = 1; i < len; ++i) 16 { 17 int j = (c << 1) - i; 18 if(j >= 0 && i + p[j] < r) 19 p[i] = p[j] < r - i ? p[j] : r - i; 20 else 21 { 22 for(p[i] = 0; i-p[i]-1 >= 0 && i+p[i]+1 < len && strCharp[i+p[i]+1] == strCharp[i-p[i]-1]; ++p[i]); 23 c = i; 24 r = i+p[i]; 25 l = i-p[i]; 26 } 27 maxl = maxl > p[i] ? maxl : p[i]; 28 } 29 delete p; 30 return maxl; 31 }
扩展(codility Gammar 2011)
给定一个全部由字母组成的字符串,求这样的下标对数(x,y)满足x < y并且[x...y]的字符串是回文的。如果数量超过10^8,返回-1。
p数组告诉我们什么?
如果p[i] = 6表示,我们实际上有一个以i为中心长度为6的回文串,那么长度为4和2依然是回文的,实际上包含了3个回文串。
如果p[i] = 5,实际上我们有3个长度分别为5,3,1的回文串。
我们不要长度为1的,也就是说我们求的是所有p[i]/2的和。