参考链接
http://blog.csdn.net/doc_sgl/article/details/9066443
http://blog.csdn.net/feliciafay/article/details/16984031 5种方法
http://leetcode.com/2011/11/longest-palindromic-substring-part-ii.html
题目描述
Longest Palindromic Substring
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
题目分析
下面介绍一种复杂度只有O(n)的Manacher算法:
首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号。比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#。 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如$#a#b#a#。
下面以字符串12212321为例,经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";
然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i]),比如S和P的对应关系:
可以看出,P[i]-1正好是原字符串中回文串的总长度,可证明。
那么怎么计算P[i]呢?该算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],表示在i之前的回文串中延伸至最右端的位置也就是最大回文子串的边界。- for(int i = 1; i < len; ++i)
- {
- if(mx > i)
- P[i] = MIN(P[id*2-i] , mx-i);
- else
- P[i] = 1;
- while(S[i+P[i]] == S[i-P[i]])
- ++P[i];
- if(i + P[i] > mx)
- {
- mx = i + P[i];
- id = i;
- }
- }
算法的关键点在这里:如果mx>i,那么P[i] >= MIN(P[2*id-i], mx-i)。
代码:
- int longestPalindromic(string str)
- {
- int strsz = str.size();
- if(strsz<=1)return strsz;
- int len = strsz*2+2;
- int* s = new int[len+1];
- s[0]='$';
- for(int i=1,j=0; j<strsz; j++)
- {
- s[i++] = '#';
- s[i++] = str[j];
- }
- s[len-1]='#';
- s[len]='\0';
- int id = 0;
- int mx = 0;
- int longestnum = 1;
- int longestid = 0;
- int* p = new int[len];
- for(int i=1; i<len; i++)
- {
- if(mx>i)
- p[i]=min(p[id*2-i],mx-i);
- else
- p[i]=1;
- while(s[i+p[i]] == s[i-p[i]])
- p[i]++;
- if(p[i]+i>mx)
- {
- mx=p[i]+i;
- id=i;
- }
- if(p[i]>longestnum)
- {
- longestnum=p[i];
- longestid = i;
- }
- }
- delete[] p;
- delete[] s;
- cout<<"Longest Palindromic Num:"<<longestnum-1<<endl;
- cout<<str.substr((longestid-1)>>1-(longestnum-1)>>1,longestnum-1)<<endl;
- return longestnum-1;
- }
其实这题也可以用后缀数组来做,但是后缀数组的复杂度太高:翻转拼接(O(n)),排序(O(nlogn)),找最长的串(O(2n*len)),找的时候还要注意一个相邻的两个串一个有#一个没有#。
总结
代码示例
// 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[n];
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);
}
TML
class Solution {
public:
string longestPalindrome(string s) {
int size = s.size();
if(size < 2) return s;
int maxlen = 0;
int begin = 0;
for(int i = 0;i<size;i++)
{
int tmplen = 0,tmpb = 0;
for(int j = 0;j<size-i;j++)
{
if(s[j] == s[size- i - j -1])
{
if(tmplen == 0) tmpb = j;
tmplen++;
}
else
tmplen = 0;
if(tmplen > maxlen)
{
maxlen = tmplen;
begin = tmpb;
}
}
if(maxlen == size-i)
break;
}
for(int i = 1;i<size;i++)
{
int tmplen = 0,tmpb = 0;
for(int j = i;j<size;j++)
{
if(s[j] == s[size-j+i-1])
{
if(tmplen == 0) tmpb = j;
tmplen++;
}
else
tmplen = 0;
if(tmplen > maxlen)
{
maxlen = tmplen;
begin = tmpb;
}
}
if(maxlen == size-i)
break;
}
string ret(s,begin,maxlen);
return ret;
}
};
推荐学习C++的资料
C++标准函数库
在线C++API查询
map使用方法
queue使用方法
vector使用方法