您的位置:首页 > 其它

Leetcode---Longest Palindromic Substring

2016-09-29 15:04 453 查看

最长回文字串(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.

题目连接:https://leetcode.com/problems/longest-palindromic-substring/

    大意是给定一个字符串S,求S中最长的回文字串,回文字符串是指从坐到右和从右到左都是一样的字符串,例如:“西湖回游鱼游回湖西”、“ABDCDBA”、“abccba”等等诸如此类的字符串,单个字符的串,也是回文串。

思路1:暴力解法,枚举S的所有字串,判断每个字符串是否是回文串,返回最长的串,当字符串很长的时候,枚举也只能想想了,更别说还要判断是否是回文。
思路2:枚举中心法,我们感兴趣的只是串中的回文字串,因为回文串的性质,那么我们可以枚举串中每一个字符,来检查以它为中心的串是不是回文串,并选取其中最长的返回。
     
     例如:ABDCDBA  
   
1.以A为中心,A的左边没有字符,所以只能是A当作一个回文。
2.以B为中心,那么比较B的左边和右边字符是否相等。相等,是回文。否则,不是。
3.以此类推,直到所有的字符作为中心判断完。

假设以i为中心,j为i左右的长度,且满足条件1:(i-j)>=0 && (i+j)<S.size(); j从0开始,到不能满足条件1结            束,分别判断,判断完成以i为中心后,以i+1为中心再进行类似判断。当然还有一个问题,就是回文串的长度有          可能是奇数,也可能是偶数。例如ABA、ABBA都是回文。这个时候只需要对奇数还是偶数进行分别判断:
               奇数时:判断S[i-j] == S[i+j],j满足(i-j)>=0 && (i+j)<S.size()。
       偶数使:判断S[i-j] == S[i+j+1],j满足(i-j)>=0 && (i+j+1)<S.size()

CODE: 
             

string LongestPalindrome( string s)
{
int start=0,maxLen = 1,tlen = 1,ti=0;
int size = s.size();

if( size <= 1 )
return s;

// 奇数
for( int i = 0; i < size; i++)
{
for( int j = 0; (i-j>=0) && (i+j= maxlen )
{
maxlen = tlen;
start = i-j;
}

// 偶数
for( int j = 0; (i-j>=0) && (i+j+1 <= n); ++j )
{
if( s[i-j] != s[i+j+1])
break;
tlen = 2*j+2;
ti = i-j;
}

if(tlen > maxlen )
{
maxlen = tlen;
start = ti;
}
}

return s.substr(start,maxlen);

}


思路3:先看下面的例子:
     A  AABABBAABCBABC
....
      可以看出什么? A按规定是一个回文,要判断BAB是不是回文,那么判断B==B,且A是不是回文。判断BAAB,则判断B==B? 且AA是不是回文。以此类推,可以总结    到:
1.单个字符是回文。
2.两个字符相同是回文,否则不是。
3.长度大于等于3的串,满足s[0]==s[s.size-1] && s.substr(1,s.size-2)是不是回文。满足则是,否则不是。
   更一般化来讲:
对于以i为起点,j为长度的string是不是回文串,只需要判断,s[i]==s[s+j-1]且s[i+1 to i+j-2]之间的串是不是回文串,这样问题规模就减小了,对于规模    大的问题转换到小规模问题求解,那么很容易想到的就是动态规划。
    DP[i][i] = true;    i<s.size();
DP[i][i+1] = true;  if( s[i]==s[i+1] && i<s.size()-1)
DP[i][j] = true;    if( s[i]==s[j] && DP[i+1][j-1] && i<size & j < size)
   按照这个思路很自然可以得出代码:
   CODE
   
string longestPalindrome(string s) {

const int size = s.size();
if( size == 1)
return s;

bool dp[1000][1000] ;

for( int i = 0; i < 1000; i++)
memset(dp[i],false,1000);

int maxlen =1;
int start = 0;

for( int i = 0; i < 1000; i++)
{
dp[i][i]= true;
if( i<(size-1) && (s[i] == s[i+1]))
{
dp[i][i+1]=true;
start = i;
maxlen = 2;
}
}

for( int len = 3; len <= size; len++)
{
for(int i = 0; i < size-len+1; i++)
{
int j = i+len-1;
if( dp[i+1][j-1] && (s.at(i)==s.at(j)))
{
dp[i][j] = true;
start = i;
maxlen = len;
}
}
}

if( maxlen >= 2)
{
return s.substr(start,maxlen);
}

return NULL;
}


  抛开暴力枚举的方法,因为效率太低了,中心枚举和动态规划的时间复杂度都是O(n^2),还有没有更高效的方法呢?当然有,这就是传说中的Manacher算法,这个方法需要点想象力啊。

思路4:Manacher算法,这就是个传说。Manacher算法思想是来自上述“中心扩展法”,在“中心法”中要考虑回文
串是奇数还是偶数,有没办法统一处理呢?有,这就是Manacher算法。

   
该算法首先将字符串的每个字符之间加入一个特殊字符,并且在字符串开头加入一个标志来更好处理越界和编码问题。例如:s=“abba”编码后是:s=“$#a#b#b#a#”。
   然后,用一个数组p来记录每个字符是s[i]为中心的的回文串向左或向右的长度,则原字符串s的回文长度为max(p[i]-1)。因为对于一个回文字符串s加入特殊字符后,该回文串加上特殊字符后依然会是一个回文串,且编码后的回文串的长度为2*s.size+1。那么数组记录的最大值为s.size+1,比原回文的长度多1。故求取p[]的最大值减1就是原字符串中最大的回文串。
   现在最重要的问题就是怎么求解p[],这似乎又回到原来开始的问题了,非也。因为加了特殊字符,会出现一些有用的结论。
   假设现在求p[i]的值,则p[0]...p[i-1]的值已经求出,maxlen为目前已经求出的回文串延伸到右边的最大长度,id为p[0]到p[i-1]最大回文串的中心,j为i关于id的对称点,即j=2*id-i。那么:
   1.如果i不在maxlen范围内,即不在任何回文串中,则p[i]=1(本身为回文)。接着判断p[i+j]==p[i-j],为真则++p[i],否则2;
   2.p[i]>=min(p[2id-i],maxlen-i]。

   证明:
情况1:
        


情况2:



情况3:



int Manacher(string s)
{
int len = s.size();
int maxLen = 0;
int id;
int mx = 0;

for (int i = 1; i < len; i++)
{
if (i < maxLen)
p[i] = min(p[2 * id - i], maxLen - i);
else
p[i] = 1;

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

if (maxLen < i + p[i])
{
id = i;
maxLen = i + p[i];
}

mx = max(mx, p[i] - 1);
}

return mx;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息