|
| 1 | +# 最长回文子字符串 第一部 |
| 2 | + |
| 3 | +http://leetcode.com/2011/11/longest-palindromic-substring-part-i.html |
| 4 | + |
| 5 | +> 给定一个字符串S,找出S中最长的回文子字符串。 |
| 6 | +
|
| 7 | +这道有趣的题目已经成为著名的Greplin http://baike.baidu.com/view/5302414.htm?fr=iciba 编程挑战的专题,而且多次在面试中被问起。为什么?因为该题有多少解法。我知道的解法就有5种。你准备接受挑战吗? |
| 8 | + |
| 9 | +## 提示 |
| 10 | +首先,确保你明白回文的意思。回文是一个字符串,正着读反着读都一样。比如,aba是一个回文,abc就不是。 |
| 11 | + |
| 12 | +## 通病 |
| 13 | +试图想出快速解法的,都不幸的答错了(但是很容易被纠正): |
| 14 | + |
| 15 | +``` |
| 16 | +反转S变成S’。找出S和S’最长的公共子字符串 http://en.wikipedia.org/wiki/Longest_common_substring 就是最长回文子字符串。 |
| 17 | +``` |
| 18 | + |
| 19 | +看上去可以工作,让我们看下面的例子。 |
| 20 | + |
| 21 | +例如, |
| 22 | +S = “caba” S’ = “abac” |
| 23 | +S和S’最长的公共子字符串是“aba”。 |
| 24 | + |
| 25 | +让我们试试另一个例子: |
| 26 | +S = “abacdfgdcaba” S’ = “abacdgfdcaba” |
| 27 | +S和S’最长的公共子字符串是“abacd”。很明显这不是一个回文。 |
| 28 | + |
| 29 | +我们发现只要在S中存在一个反转的非回文子字符串,最长公共子字符串的解法就失败了。修正该解法,每次我们找到一个最长公共子字符串,我们检查一下该子字符串和反转子字符串之前的原始字符串是否一样。如果一样,我们更新当前找到的最长公共子字符串;如果不一样,我们跳过它寻找下一个。 |
| 30 | + |
| 31 | +该解法时间复杂度是O(N2)使用O(N2)空间(可以改进只使用O(N)空间)。请阅读最长公共子字符串 http://en.wikipedia.org/wiki/Longest_common_substring |
| 32 | + |
| 33 | +## 暴力解法,时间复杂度O(N3) |
| 34 | +暴力解法就是穷举所有可能的子字符串的起止位置,然后挨个检查是否是回文。总共C(N, 2)子字符串(单个字符除外,它不算回文)。 |
| 35 | + |
| 36 | +挨个检查每个子字符串的时间复杂度是O(N),运行时复杂度是O(N3)。 |
| 37 | + |
| 38 | +## 动态编程解法 O(N2)时间复杂度 O(N2)空间复杂度 |
| 39 | +改进数据处理的暴力解法,首先考虑我们如何省去检查是否是回文的不必要的重复计算。考虑“ababa”。如果我们已经知道“bab”是回文,很明显“ababa”肯定是回文,因为左右两端的字母是一样的。 |
| 40 | + |
| 41 | +公式化如下所示: |
| 42 | + |
| 43 | +``` |
| 44 | +定义P[ i, j ] ← true 如果子字符串Si … Sj是回文,否则false. |
| 45 | +``` |
| 46 | + |
| 47 | +所以, |
| 48 | + |
| 49 | +``` |
| 50 | +P[ i, j ] ← ( P[ i+1, j-1 ] && Si = Sj ) |
| 51 | +``` |
| 52 | + |
| 53 | +基本如下: |
| 54 | + |
| 55 | +``` |
| 56 | +P[ i, i ] ← true |
| 57 | +P[ i, i+1 ] ← ( Si = Si+1 ) |
| 58 | +``` |
| 59 | + |
| 60 | +这就产生了一个直接的数据处理解法,我们首先初始化一个和两个字母的回文,然后找出所有三个字母的回文,N个... |
| 61 | + |
| 62 | +时间复杂度O(N2),空间复杂度O(N2) |
| 63 | + |
| 64 | +``` |
| 65 | +string longestPalindromeDP(string s) { |
| 66 | + int n = s.length(); |
| 67 | + int longestBegin = 0; |
| 68 | + int maxLen = 1; |
| 69 | + bool table[1000][1000] = {false}; |
| 70 | + for (int i = 0; i < n; i++) |
| 71 | + table[i][i] = true; |
| 72 | + for (int i = 0; i < n-1; i++) { |
| 73 | + if (s[i] == s[i+1]) { |
| 74 | + table[i][i+1] = true; |
| 75 | + longestBegin = i; |
| 76 | + maxLen = 2; |
| 77 | + } |
| 78 | + } |
| 79 | + for (int len = 3; len <= n; len++) { |
| 80 | + for (int i = 0; i < n-len+1; i++) { |
| 81 | + int j = i + len - 1; |
| 82 | + if (s[i] == s[j] && table[i+1][j-1]) { |
| 83 | + table[i][j] = true; |
| 84 | + longestBegin = i; |
| 85 | + maxLen = len; |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + return s.substr(longestBegin, maxLen); |
| 90 | +} |
| 91 | +``` |
| 92 | + |
| 93 | +## 附加题 |
| 94 | +你能改进上面的解法,如何减少空间复杂度? |
| 95 | + |
| 96 | +## 一个更加简单的解法O(N2)时间复杂度O(1)空间复杂度 |
| 97 | +实际上,我们可以在O(N2)时间复杂度,不使用额外空间的前提下解题。 |
| 98 | + |
| 99 | +我们发现一个回文是中心点对称的。所以,一个回文可以由中心点扩展,而且只有2N - 1个中心点。 |
| 100 | + |
| 101 | +你可能会问为什么有2N - 1个中心点,而不是N个?这是因为一个回文的中心点可能在两个字母之间。有偶数个字母的回文(比如“abba”)它的中心点在两个b之间。 |
| 102 | + |
| 103 | +由回文中心点扩展开来,时间复杂度变为O(N),总复杂度是O(N2)。 |
| 104 | + |
| 105 | +``` |
| 106 | +string expandAroundCenter(string s, int c1, int c2) { |
| 107 | + int l = c1, r = c2; |
| 108 | + int n = s.length(); |
| 109 | + while (l >= 0 && r <= n-1 && s[l] == s[r]) { |
| 110 | + l--; |
| 111 | + r++; |
| 112 | + } |
| 113 | + return s.substr(l+1, r-l-1); |
| 114 | +} |
| 115 | + |
| 116 | +string longestPalindromeSimple(string s) { |
| 117 | + int n = s.length(); |
| 118 | + if (n == 0) return ""; |
| 119 | + string longest = s.substr(0, 1); // 单个字符本身是个回文 |
| 120 | + for (int i = 0; i < n-1; i++) { |
| 121 | + string p1 = expandAroundCenter(s, i, i); |
| 122 | + if (p1.length() > longest.length()) |
| 123 | + longest = p1; |
| 124 | + |
| 125 | + string p2 = expandAroundCenter(s, i, i+1); |
| 126 | + if (p2.length() > longest.length()) |
| 127 | + longest = p2; |
| 128 | + } |
| 129 | + return longest; |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +## 进一步思考 |
| 134 | +是否存在O(N)的解法?你打赌!但是,它需要非常聪明的洞察力。第二部 https://github.com/xiangzhai/leetcode/blob/master/question/longest-palindromic-substring-part-ii.md 将讲解O(N)的解法。 |
0 commit comments