LeetCode OJ:Longest Palindromic Substring

2024-02-01 05:38

本文主要是介绍LeetCode OJ:Longest Palindromic Substring,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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.


算法思想:

动态规划,用时484ms

class Solution {  
public:  string longestPalindrome(string s) {  int n = s.length();  bool dp[1000][1000];  int start = 0;  int max = 1;  for (int j = 0; j < n; ++j)  {  for (int i = 0; i <= j; ++i)  {  if (( j-i<= 1 || dp[i+1][j-1]) && s[i] == s[j])  {  dp[i][j] = true;  if (j-i+1 > max)  {  start = i;  max = j-i+1;  }  }  else dp[i][j] = false;  }  }  return s.substr(start,max);  }  
};  

2、网上有人贴出了中心向外拓展的方法,也不错,用时140ms,思路挺简单,只是对奇偶个回文串需要注意

class Solution {  
public:  string longestPalindrome(string s) {  size_t n = s.length();  int startPos = 0;  int max = 1;  for (int i = 0; i < n; ++i)  {  int oddLen = 0, evenLen = 0, curLen;  oddLen = Palindromic(s,i,i);  if (i + 1 < n)  evenLen = Palindromic(s,i,i+1);  curLen = oddLen > evenLen? oddLen : evenLen;  if (curLen > max)  {  max = curLen;  if (max & 0x1)  startPos = i - max / 2;  else   startPos = i - (max - 1) / 2;  }  }  return s.substr(startPos,max);  }  int Palindromic(const string &str, int i, int j)  {  size_t n = str.length();  int curLen = 0;  while (i >= 0 && j < n && str[i] == str[j])  {  --i;  ++j;  }  curLen = (j-1) - (i+1) + 1;  return curLen;  }  
};  

3、网上有人贴出用KMP算法实现的解答 最长连续回文串(Longest Palindromic Substring)

我将java版的改成c++版的并优化了下,算法是可以的,但是还是TLE

class Solution {  int *nextval;
public:  void get_nextval(char const* ptrn, int plen, int* nextval)        {        int i = 0;         nextval[i] = -1;        int j = -1;        while( i < plen-1 )        {        if( j == -1 || ptrn[i] == ptrn[j] )       {        ++i;        ++j;        if( ptrn[i] != ptrn[j] )         nextval[i] = j;              else        nextval[i] = nextval[j];        }        else                                         j = nextval[j];        }        }    int KMP(const char *S, const char *T) {    int slen = strlen(S);    int tlen = strlen(T);    int i = 0 ,j = 0;    int maxLen=0;//int *nextval=new int[tlen];    get_nextval(T,tlen,nextval);    while ( i+j < slen && j < tlen){    if ( j == -1 || S[i+j] == T[j] ){    j ++;    }    else    {    i ++;    j = nextval[j];    }    if( j > maxLen)  {  maxLen = j;  }  if(j == tlen)  {  return maxLen;  }  }    return maxLen;   } string longestPalindrome(string s) {  string str=s;const char *S=str.c_str();int size=s.length();reverse(s.begin(),s.end());  //求得到 输入string 的reverse  nextval = new int[1000];  int start = 0;  int maxLen = 0;  int len;  for(int i = 0; i < size; i++) //枚举所有后缀  {    if( size - i < maxLen)  {  break;  }  len = KMP(S, &s[i]);  if( len > maxLen)  {  start = i; maxLen = len;  }  }  return s.substr(start,maxLen);  }  
};  


思路4. 算法来源于此

http://www.leetcode.com/2011/11/longest-palindromic-substring-part-ii.html

不过原文的陈述仔细研究了一下,有一些地方让人着实费解,所以自己决定重写一遍。

这里描述了一个叫Manacher’s Algorithm的算法。

算法首先将输入字符串S, 转换成一个特殊字符串T,转换的原则就是将S的开头结尾以及每两个相邻的字符之间加入一个特殊的字符,例如#

例如: S = “abaaba”, T = “#a#b#a#a#b#a#”.

为了找到最长的回文字串,例如我们当前考虑以Ti为回文串中间的元素,如果要找到最长回文字串,我们要从当前的Ti扩展使得 Ti-d … Ti+d 组成最长回文字串. 这里 d 其实和 以Ti为中心的回文串长度是一样的. 进一步解释就是说,因为我们这里插入了 # 符号,对于一个长度为偶数的回文串,他应该是以#做为中心的,然后向两边扩,对于长度是奇数的回文串,它应该是以一个普通字符作为中心的。通过使用#,我们将无论是奇数还是偶数的回文串,都变成了一个以Ti为中心,d为半径两个方向扩展的问题。并且d就是回文串的长度。

例如 #a#b#a#, P = 0103010, 对于b而言P的值是3,是最左边的#,也是延伸的最左边。这个值和当前的回文串是一致的。

如果我们求出所有的P值,那么显然我们要的回文串,就是以最大P值为中心的回文串。

T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0

例如上面的例子,最长回文是 “abaaba”, P6 = 6.

根据观察发现,如果我们在一个位置例如 abaaba的中间位置,用一个竖线分开,两侧的P值是对称的。当然这个性质不是在任何时候都会成立,接下来就是分析如何利用这个性质,使得我们可以少算很多P的值。

下面的例子 S = “babcbabcbaccba” 存在更多的折叠回文字串。


C表示当前的回文中心,L和R处的线表示以C为中心可以到达的最左和最右位置,如果知道这些,我们如何可以更好的计算C后面的P[i]. 
假设我们当前计算的是 i = 13, 根据对称性,我们知道对称的那个下标 i' = 9. 

根据C对称的原则,我们很容易得到如下数据  P[ 12 ] = P[ 10 ] = 0, P[ 13 ] = P[ 9 ] = 1, P[ 14 ] = P[ 8 ] = 0).

Now we are at index i = 15, and its mirrored index around C is i’ = 7. Is P[ 15 ] = P[ 7 ] = 7?

当时当i = 15的时候,却只能得到回文 “a#b#c#b#a”, 长度是5, 而对称 i ' = 7 的长度是7. 


如上图所示,如果以 i, i' 为中心,画出对称的区域如图,其中以i‘ = 7 对称的区域是 实心绿色 + 虚绿色 和 左侧,虚绿色表示当前的对称长度已经超过之前的对称中心C。而之前的P对称性质成立的原因是 i 右侧剩余的长度 R - i 正好比 以 i‘ 为中心的回文小。 
这个性质可以这样归纳,对于 i 而言,因为根据C对称的最右是R,所以i的右侧有 R - i 个元素是保证是 i' 左侧是对称的。 而对于 i' 而言他的P值,也就是回文串的长度,可能会比 R-i 要大。 如果大于 R - i, 对于i而言,我们只能暂时的先填写 P[i] = R - i, 然后依据回文的属性来扩充P[i] 的值; 如果P[i '] 小于R-i,那么说明在对称区间C内,i的回文串长度和i' 是一样长的。例如我们的例子中 i = 15, 因为R = 20,所以i右侧 在对称区间剩余的是 R - 15 = 5, 而 i’ = 7 的长度是7. 说明 i' 的回文长度已经超出对称区间。我们只能使得P[i] 赋值为5, 然后尝试扩充P[i]. 
if P[ i' ] ≤ R – i,
then P[ i ] ← P[ i' ]
else P[ i ] ≥R – i. (这里下一步操作是扩充 P[ i ].

扩充P[i] 之后,我们还要做一件事情是更新 R 和 C, 如果当前对称中心的最右延伸大于R,我们就更新C和R。在迭代的过程中,我们试探i的时候,如果P[i'] <= R - i, 那么只要做一件事情。 如果不成立我们对当前P[i] 做扩展,因为最大长度是n,扩展最多就做n次,所以最多做2*n。 所以最后算法复杂度是 O(n)


[cpp]  view plain copy
  1. // Transform S into T.  
  2. // For example, S = "abba", T = "^#a#b#b#a#$".  
  3. // ^ and $ signs are sentinels appended to each end to avoid bounds checking  
  4. string preProcess(string s) {  
  5.   int n = s.length();  
  6.   if (n == 0) return "^$";  
  7.   string ret = "^";  
  8.   for (int i = 0; i < n; i++)  
  9.     ret += "#" + s.substr(i, 1);  
  10.    
  11.   ret += "#$";  
  12.   return ret;  
  13. }  
  14.    
  15. string longestPalindrome(string s) {  
  16.   string T = preProcess(s);  
  17.   int n = T.length();  
  18.   int *P = new int[n];  
  19.   int C = 0, R = 0;  
  20.   for (int i = 1; i < n-1; i++) {  
  21.     int i_mirror = 2*C-i; // equals to i' = C - (i-C)  
  22.    
  23.     P[i] = (R > i) ? min(R-i, P[i_mirror]) : 0;  
  24.    
  25.     // Attempt to expand palindrome centered at i  
  26.     while (T[i + 1 + P[i]] == T[i - 1 - P[i]])  
  27.       P[i]++;  
  28.    
  29.     // If palindrome centered at i expand past R,  
  30.     // adjust center based on expanded palindrome.  
  31.     if (i + P[i] > R) {  
  32.       C = i;  
  33.       R = i + P[i];  
  34.     }  
  35.   }  
  36.    
  37.   // Find the maximum element in P.  
  38.   int maxLen = 0;  
  39.   int centerIndex = 0;  
  40.   for (int i = 1; i < n-1; i++) {  
  41.     if (P[i] > maxLen) {  
  42.       maxLen = P[i];  
  43.       centerIndex = i;  
  44.     }  
  45.   }  
  46.   delete[] P;  
  47.    
  48.   return s.substr((centerIndex - 1 - maxLen)/2, maxLen);  
  49. }  

这篇关于LeetCode OJ:Longest Palindromic Substring的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/666234

相关文章

Java 字符串操作之contains 和 substring 方法最佳实践与常见问题

《Java字符串操作之contains和substring方法最佳实践与常见问题》本文给大家详细介绍Java字符串操作之contains和substring方法最佳实践与常见问题,本文结合实例... 目录一、contains 方法详解1. 方法定义与语法2. 底层实现原理3. 使用示例4. 注意事项二、su

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

usaco 1.2 Palindromic Squares(进制转化)

考察进制转化 注意一些细节就可以了 直接上代码: /*ID: who jayLANG: C++TASK: palsquare*/#include<stdio.h>int x[20],xlen,y[20],ylen,B;void change(int n){int m;m=n;xlen=0;while(m){x[++xlen]=m%B;m/=B;}m=n*n;ylen=0;whi

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param

LeetCode:64. 最大正方形 动态规划 时间复杂度O(nm)

64. 最大正方形 题目链接 题目描述 给定一个由 0 和 1 组成的二维矩阵,找出只包含 1 的最大正方形,并返回其面积。 示例1: 输入: 1 0 1 0 01 0 1 1 11 1 1 1 11 0 0 1 0输出: 4 示例2: 输入: 0 1 1 0 01 1 1 1 11 1 1 1 11 1 1 1 1输出: 9 解题思路 这道题的思路是使用动态规划