最长回文子串第一部分(leetcode)

原始链接来自:

http://articles.leetcode.com/longest-palindromic-substring-part-i


最长回文串I

给定一个字符串S,找到S中最长的回文子串。

这个有趣的问题已经在着名的Greplin编程挑战中出现过,而且在面试中也经常被问到。为什么?因为这个问题可以在许多方面对你进行考验。目前我已知有五个不同的解决方案。你是否接受这个挑战?

转到在线法官现在解决它!(您可以提交C ++或Java解决方案)

提示:
首先,确保你明白回文串的含义。回文串是在两个方向上读起来都相同的字符串。例如,“aba”是回文串,“abc”不是。

一个常见的错误:
有些人会快速地想出一个解决方案,不幸的是含有缺陷(但可以很容易纠正):

反转S并变成S'。找到S和S'之间 最长的公共子字符串,它一定是最长的回文子字符串。

这看起来奏效,但是让我们看看下面的例子。

例如,
S =“caba”,S'=“abac”。
S和S'之间最长的共同子串是“aba”,这是答案。

让我们尝试另一个例子:
S =“abacdfgdcaba”,S'=“abacdgfdcaba”。
S和S'之间最长的公共子串是“abacd”。显然,这不是一个有效的回文。

我们可以看到,当在S的某些其他部分中存在非回文子串的逆转串时,最长公共子串方法无法奏效。为了纠正这种情况,每次我们找到最长的公共子串候选时,我们检查子字符串的索引是否与反向子字符串的原始索引相同。如果是,那么我们尝试更新到目前为止找到的最长的回文; 如果没有,我们跳过这个,找到下一个候选。

这个方法是复杂度O(N 2)的DP解决方案(可以改进以使用O(N)空间)。请阅读更多关于最长公共子串这里

暴力解决方案,O(N 3):
明显的暴力解决方案是选取子字符串的所有可能的开始和结束位置,并验证它是否是回文。总共有C(N,2)个这样的子串(不包括字符本身是回文的情况)。

由于验证每个子串需要O(N)时间,所以运行时间复杂度是O(N 3)。

动态规划解决方案,O(N 2)时间和O(N 2)空间:
为了改进来自DP方法的强力解决方案,首先想想如何避免在验证回文中进行不必要的重复计算。考虑“ababa”的情况。如果我们已经知道“bab”是回文,显然“ababa”必须是回文,因为两个左右端字母是相同的。

更正式地描述如下:

定义P [i,j]← 如果子串S  i ... S  j是回文则为真,否则为假。

因此,

P [i,j]←(P [i + 1,j-1]   S  i = S  j

基本情况是:

P [i,i]←真
P [i,i + 1]←(S  i = S  i + 1

这产生一个直接的DP解决方案,我们首先初始化一个和两个字母回文,并努力找到所有三个字母回文,等等... 

这为我们提供了O(N 2)的运行时复杂度,并使用O(N 2)空间来存储表。

string longestPalindromeDP(string s) {
  int n = s.length();
  int longestBegin = 0;
  int maxLen = 1;
  bool table[1000][1000] = {false};
  for (int i = 0; i < n; i++) {
    table[i][i] = true;
  }
  for (int i = 0; i < n-1; i++) {
    if (s[i] == s[i+1]) {
      table[i][i+1] = true;
      longestBegin = i;
      maxLen = 2;
    }
  }
  for (int len = 3; len <= n; len++) {
    for (int i = 0; i < n-len+1; i++) {
      int j = i+len-1;
      if (s[i] == s[j] && table[i+1][j-1]) {
        table[i][j] = true;
        longestBegin = i;
        maxLen = len;
      }
    }
  }
  return s.substr(longestBegin, maxLen);
}


附加练习:
你能进一步改进上述空间复杂度和时间复杂度吗?

一个更简单的方法,O(N 2)时间和O(1)空间:
事实上,我们可以解决它在O(N 2)时间且无需任何额外的空间。

我们观察到回文都是围绕它的中心展开的。因此,回文可以从其中心扩展,并且只有2N-1个这样的中心。

你可能会问为什么有2N-1但不是N中心?原因是回文中心可以在两个字母之间。这样的回文具有偶数个字母(例如“abba”),并且它的中心在两个b之间。

因为围绕其中心扩展回文需要花费O(N)时间,所以总体复杂度是O(N 2)。

string expandAroundCenter(string s, int c1, int c2) {
  int l = c1, r = c2;
  int n = s.length();
  while (l >= 0 && r <= n-1 && s[l] == s[r]) {
    l--;
    r++;
  }
  return s.substr(l+1, r-l-1);
}
 
string longestPalindromeSimple(string s) {
  int n = s.length();
  if (n == 0) return "";
  string longest = s.substr(0, 1);  // a single char itself is a palindrome
  for (int i = 0; i < n-1; i++) {
    string p1 = expandAroundCenter(s, i, i);
    if (p1.length() > longest.length())
      longest = p1;
 
    string p2 = expandAroundCenter(s, i, i+1);
    if (p2.length() > longest.length())
      longest = p2;
  }
  return longest;
}


进一步的想法:

O(N)解决方案是否存在?猜一下吧!然而,这不是很容易解释清楚的的,需要一些非常聪明的观察。O(N)解决方案在我的下一篇文章中解释

»继续阅读 Longest Palindromic Substring Part II。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值