【LeetCode】Longest Palindromic Substring

17 篇文章 0 订阅
11 篇文章 0 订阅

参考链接

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之前的回文串中延伸至最右端的位置也就是最大回文子串的边界。

  1. for(int i = 1; i < len; ++i)  
  2.     {  
  3.         if(mx > i)  
  4.             P[i] = MIN(P[id*2-i] , mx-i);  
  5.         else  
  6.             P[i] = 1;  
  7.         while(S[i+P[i]] == S[i-P[i]])  
  8.             ++P[i];  
  9.         if(i + P[i] > mx)  
  10.         {  
  11.             mx = i + P[i];  
  12.             id = i;  
  13.         }  
  14.     }  

算法的关键点在这里:如果mx>i,那么P[i] >= MIN(P[2*id-i], mx-i)。


代码:

  1. int longestPalindromic(string str)  
  2. {  
  3.     int strsz = str.size();  
  4.     if(strsz<=1)return strsz;  
  5.     int len = strsz*2+2;  
  6.     int* s = new int[len+1];  
  7.     s[0]='$';  
  8.     for(int i=1,j=0; j<strsz; j++)  
  9.     {  
  10.         s[i++] = '#';  
  11.         s[i++] = str[j];  
  12.     }  
  13.     s[len-1]='#';  
  14.     s[len]='\0';  
  15.   
  16.     int id = 0;  
  17.     int mx = 0;  
  18.     int longestnum = 1;  
  19.     int longestid = 0;  
  20.     int* p = new int[len];  
  21.     for(int i=1; i<len; i++)  
  22.     {  
  23.         if(mx>i)  
  24.             p[i]=min(p[id*2-i],mx-i);  
  25.         else  
  26.             p[i]=1;  
  27.         while(s[i+p[i]] == s[i-p[i]])  
  28.             p[i]++;  
  29.         if(p[i]+i>mx)  
  30.         {  
  31.             mx=p[i]+i;  
  32.             id=i;  
  33.         }  
  34.         if(p[i]>longestnum)  
  35.         {  
  36.             longestnum=p[i];  
  37.             longestid = i;  
  38.         }  
  39.     }  
  40.     delete[] p;  
  41.     delete[] s;  
  42.     cout<<"Longest Palindromic Num:"<<longestnum-1<<endl;  
  43.     cout<<str.substr((longestid-1)>>1-(longestnum-1)>>1,longestnum-1)<<endl;  
  44.     return longestnum-1;  
  45. }  

其实这题也可以用后缀数组来做,但是后缀数组的复杂度太高:翻转拼接(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;
    }
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值