这几天在刷leetcode,碰到了最长回文字串问题,网上查资料发现至少有4种解法,在学习完每种方法之后,写个博客总结下。参考了别人写的博客,可能会有些雷同。
参考资料:http://blog.csdn.net/kangroger/article/details/37742639
http://www.cnblogs.com/biyemyyhjob/archive/2012/10/04/2711527.html
最长回文子串问题:给出一个字符串str,求它的最长回文字串。回文字符串是指顺序和逆序是同一字符串的字符串。
解法1:暴力求解法
根据习惯,首先想一个暴力解法,这个问题的暴力解法思想是这样:遍历每一个子串,判断是否是回文串,并维护一个最长回文起始地址和最大回文长度。求每一个子串时间复杂度O(N^2),判断子串是不是回文O(N),两者是相乘关系,所以时间复杂度为O(N^3)。代码如下:
string longestPalindrome(string s) {
int len = s.size();//字符串长度
if (len == 0) return " ";
int maxlen = 0;//最长回文字符串长度
int start;//最长回文字符串起始地址
for (int i = 0; i<len; i++)//起始地址
for (int j = i; j < len; j++)//结束地址
{
int tmp1, tmp2;
for (tmp1 = i, tmp2 = j; tmp1 <= tmp2; tmp1++, tmp2--)//判断是不是回文
{
if (s.at(tmp1) != s.at(tmp2))
break;
}
if (tmp1 > tmp2 && j - i + 1 > maxlen)
{
maxlen = j - i + 1;
start = i;
}
}
return s.substr(start, maxlen);//求子串
}
解法2:动态规划法
回文字符串的以中心对称的子串也是回文,比如"abccba"是回文串,则"cc","bccb"也是回文。
可以用p[i][j]来表示以下标i开始,下标j结束的一个子串,则定义p[i][j]=1表示这个子串是回文串,p[i][j]=0不是回文串。可知对于所有i=0~length(str),
有p[i][i]=1,如果str[i]==str[i+1],则p[i][i+1]=1.这样就判断了长度为1和2的所有子串是不是回文串。更长长度的子串的判断,就可以根据其中心对称
子串是否是回文串以及两头的字符是否一样来判断了。空间复杂度O(N^2),时间复杂度O(N^2)。代码如下:
string longestPalindrome(string s) {
const int length = s.size();
int maxlength = 1;
int start = 0;
bool P[1000][1000] = { false };
for (int i = 0; i<length; i++)//初始化准备
{
P[i][i] = true;
if (i<length - 1 && s.at(i) == s.at(i + 1))
{
P[i][i + 1] = true;
start = i; //最后一个长度为2的回文串的起始下标
maxlength = 2;
}
}
for (int len = 3; len<=length; len++)//子串长度
for (int i = 0; i <= length - len; i++)//子串起始地址
{
int j = i + len - 1;//子串结束地址
if (P[i + 1][j - 1] && s.at(i) == s.at(j))
{
P[i][j] = true;
maxlength = len;
start = i;
}
}
return s.substr(start, maxlength);
}
解法3:中心扩展法
中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)。代码如下:
string longestPalindrome(string &s)
{
const int length = s.size();
int maxlength = 1;
int start = 0;
for (int i = 0; i < length; i++)
{
int h = i - 1, k = i, j = i + 1;
while (k >= 0 && j < length && s[k] == s[j])
{
if (maxlength <= j - k + 1)
{
maxlength = j - k + 1;
start = k;
k--;
j++;
}
else
{
k--;
j++;
}
}
h = i - 1, k = i, j = i + 1;
while (h >= 0 && j < length && s[h] == s[j])
{
if (maxlength <= j - h + 1)
{
maxlength = j - h + 1;
start = h;
h--;
j++;
}
else
{
h--;
j++;
}
}
}
return s.substr(start, maxlength);
}
解法4:manacher法
参考http://www.cnblogs.com/biyeymyhjob/archive/2012/10/04/2711527.html 代码如下:
string longestPalindrome(string &s)
{
string str;
str += "$#";
for(int i = 0; i < s.size(); i++)
{
str += s[i];
str += "#";
}
int *p = new int[str.size() + 1];
memset(p, 0, sizeof(p));
int mx = 0, id = 0;
for(int i = 1; i <= str.size(); i++)
{
if(mx > i)
{
p[i] = (p[2*id - i] < (mx - i) ? p[2*id - i] : (mx - i));
}
else
{
p[i] = 1;
}
while(str[i - p[i]] == str[i + p[i]])
p[i]++;
if(i + p[i] > mx)
{
mx = i + p[i];
id = i;
}
}
int max = 0, ii;
for(int i = 1; i < str.size(); i++)
{
if(p[i] > max)
{
ii = i;
max = p[i];
}
}
max--;
int start = ii - max ;
int end = ii + max;
char buf[1000];
memset(buf,'\0',1000);
int k=0;
for(int i = start; i <= end; i++)
{
if(str[i] != '#')
{
buf[k++] = str[i];
}
}
delete p;
return buf;
}