您的位置:首页 > 其它

LeetCode 最长回文子字符串

2014-09-03 17:49 99 查看
求一个字符串的最长回文子字符串,网上分析的文章不计其数

这里是LeetCode的原文:http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html#comment-226860

CSDN也有很多,例如这个:/article/1524677.html

九章算法,仍然借助了辅助字符串的概念,但没有实际构造一个:http://answer.ninechapter.com/solutions/longest-palindromic-substring/

虽然昨天用C完成了一份,但感觉C写这类代码真是不方便,还是java或者python比较好,改天放一个python的版本上来
对于网上的代码,补充一点分析,主要是对于开始几步。先看一下LeetCode的代码:
// Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
string preProcess(string s) {
int n = s.length();
if (n == 0) return "^$";
string ret = "^";
for (int i = 0; i < n; i++)
ret += "#" + s.substr(i, 1);
ret += "#$";
return ret;
}
string longestPalindrome(string s) {
string T = preProcess(s);
int n = T.length();
int *P = new int
;
int C = 0, R = 0;
for (int i = 1; i < n-1; i++) {
int i_mirror = 2*C-i; // equals to i' = C - (i-C)

P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;

// Attempt to expand palindrome centered at i
while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
P[i]++;
// If palindrome centered at i expand past R,
// adjust center based on expanded palindrome.
if (i + P[i] > R) {
C = i;
R = i + P[i];
}
}
// Find the maximum element in P.
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n-1; i++) {
if (P[i] > maxLen) {
maxLen = P[i];
centerIndex = i;
}
}
delete[] P;

return s.substr((centerIndex - 1 - maxLen)/2, maxLen);
}


其中,以下这段代码比较好理解,可结合各种文章的分析
while (T[i + 1 + P[i]] == T[i - 1 - P[i]])
P[i]++;
// If palindrome centered at i expand past R,
// adjust center based on expanded palindrome.
if (i + P[i] > R) {
C = i;
R = i + P[i];
}


但是比较难理解的是初始化条件和初始的判断语句
int C = 0, R = 0;
for (int i = 1; i < n-1; i++) {
int i_mirror = 2*C-i; // equals to i' = C - (i-C)

P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;
按照辅助字符串构造方法,开始时C和R位于串首'^'处,i位于第一个'#'处,i_mirror位于-1处

此时算法从第一个'#'处记录各个位置的最长回文子串的半径,其实完全可以从P[2]处也即第一个实际字符处开始记录,但这样边界条件的处理会稍微复杂一点。
记住,R代表了目前所找到的回文子串的最右边界,对于每一个原文字符串,P[i]至少为1因为两边有'#',而对每一个'#',可能为0.
所以对于i如果等于或者大于R,P[i]肯定是要重新计算了,所以要置0,而如果i没有超过R,则说明i还在当前所有回文子串的最右边界范围内,或者说T[i]位置的字符是被某一个回文子串包含了,而这个字符串的中心就是C。注意,C并不等于i,例如图中C等于11,但i等于15,C为中心的子串半径很大




此时P[i]的值可以参考它的对称点的值,如果对称点为中心的回文子串完全落在C为中心的回文子串范围内,则很显然P[i]=P[i_mirror],如果不是的话,就只能确保i为中心,R为右边界的子串为回文,至于再往两边是不是则需要程序判断了,,所以此时需要设P[i]=R-i,而判断i_mirror为中心的子串是否完全落在C为中心的回文子串范围内,就是比较R-i和P[i_mirror]的大小,实际上R-i等价于i_mirror-L

再回到开始吧,R什么时候大于i?就是找到了某个半径大于1的子串之后而i还没有突破R的边界

R什么时候会小于i?当走到右边界之后还没有找到更新的回文子串,也就是某次循环的末尾i=R,P[i]=0
当下次循环开始的时候,i=R+1,所以此时P[i]=0。
可以看出,初始化时就是i=R+1的情况,所以初始循环条件成立。

补充一下,LeetCode的源码有问题,有人看出来了吗?
说一下吧,如果原文字符串即以'^'开头,而且后面没有回文子串了,那么P[i]=1就是找到的maxLen!
此时取子串时 (centerIndex - 1 - maxLen)/2 被除数会出现负数,虽然不影响具体结果(输出第一个字符),但多多少少算不严谨吧。
解决方法,找P[i]最大值时从i=2开始找即可,因为这才是实际的第一个字符。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: