005._longest_palindromic_substring.md 8.3 KB
Newer Older
1
### 5. Longest Palindromic Substring
K
KEQI HUANG 已提交
2 3 4

题目:

K
KEQI HUANG 已提交
5
https://leetcode.com/problems/longest-palindromic-substring/
K
KEQI HUANG 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19

难度:

Medium



思路0:

暴力解法绝对不行

思路1:

所以一个好的想法是 s 和 reverse(s) 共有的最长的 substring就是longest palindromic substring -> 问题转成求Longest common substring problem
K
KEQI HUANG 已提交
20 21 22

参见wikipedia

K
KEQI HUANG 已提交
23 24 25 26 27 28 29 30
,典型动归

LCSuff(S1...p, T1...q) = LCS(S1...p1, T1...q-1) if S[p] = T[q] else 0



伪码也有了,代码也有:

K
KEQI HUANG 已提交
31
https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_substring#Python_2
K
KEQI HUANG 已提交
32 33

这样也超时?
34
```python
K
KEQI HUANG 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    class Solution(object):
        def longestPalindrome(self, s):
            """
            :type s: str
            :rtype: str
            """
            def lcs(s1, s2):
                m = [[0] * (1 + len(s2)) for i in xrange(1 + len(s1))]
                longest, x_longest = 0, 0
                for x in xrange(1, 1 + len(s1)):
                    for y in xrange(1, 1 + len(s2)):
                        if s1[x - 1] == s2[y - 1]:
                            m[x][y] = m[x - 1][y - 1] + 1
                            if m[x][y] > longest:
                                longest = m[x][y]
                                x_longest = x
                        else:
                            m[x][y] = 0
                return s1[x_longest - longest: x_longest]
    
            return lcs(s, s[::-1])
56
```
K
KEQI HUANG 已提交
57 58
因为以为这样s[::-1]已经很快了.

K
KEQI HUANG 已提交
59
这个方法是buggy的,看字符串abcxgcba,它reverse之后是abcgxcba,它们有公共字符串,但是这里面没有回文,修复方式是:
K
KEQI HUANG 已提交
60

K
KEQI HUANG 已提交
61
we check if the substring’s indices are the same as the reversed substring’s original indices. If it is, then we attempt to update the longest palindrome found so far; if not, we skip this and find the next candidate.
K
KEQI HUANG 已提交
62 63 64

我觉得的修复方式这样么:

K
KEQI HUANG 已提交
65 66 67 68 69 70 71 72 73 74
    原本     翻转
    ABXYBA   ABYXBA
    
    求出来的substring indices是 0:2 但是这个s1[0:2] 和 s2[0:2]一样,所以不行
    同理common substring indices还是s[4:6] 和s2[4:6]一样,不行
    
    而比如ABAD和 DABA
    
    substring indice 一个是0:3, 一个是1:4,这样就没问题
    
K
KEQI HUANG 已提交
75 76 77 78 79 80 81



思路2:



K
KEQI HUANG 已提交
82 83 84
依次把每一个字符当做回文字符串的中间字符,找到以该字符为中间字符的回文串的最大长度。分别对奇偶的情况进行讨论,接下来的关键就是对边界的把握,确保下标不要越界。当子串已经包含首字符或最后一个字符且此时还是回文串的时候,下标分别会向两边多移一位,需要补回来。

参考https://shenjie1993.gitbooks.io/leetcode-python/content/005%20Longest%20Palindromic%20Substring.html
85
```python
K
KEQI HUANG 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    class Solution(object):
        def longestPalindrome(self, s):
            """
            :type s: str
            :rtype: str
            """
            n = len(s)
    
            # empty or one char
            if n < 2:
                return s
    
            # left index of the target substring
            l = 0
            # right index of the target substring
            r = 0
            # length of the longest palindromic substring for now
            m = 0
            # length of the current substring
            c = 0
    
            # Whether the substring contains the first character or last character and is palindromic
            b = True
            for i in range(n):
                # Odd situation
                for j in range(min(n-i,i+1)):
                    if s[i-j] != s [i+j]:
                        b = False
                        break
                    else:
                        c = 2 * j + 1
    
                if c > m :
                    l = i - j + 1 - b
                    r = i + j + b
                    m = c 
                b = True
    
                # Even situation
                for j in range(min(n - i - 1, i + 1)):
                    if (s[i - j] != s[i + j + 1]):
                        b = False
                        break
                    else:
                        c = 2 * j + 2
                if (c > m):
                    l = i - j + 1 - b
                    r = i + j + 1 + b
                    m = c
                b = True
            return s[l:r]
137
```
K
KEQI HUANG 已提交
138
以上是参考版本,自己写的版本:
139
```python
K
KEQI HUANG 已提交
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    class Solution(object):
        def longestPalindrome(self, s):
            """
            :type s: str
            :rtype: str
            """
            n = len(s)
    
            m,l,r = 0,0,0
    
            for i in range(n):
                # odd case
                for j in range(min(i+1,n-i)):
                    if s[i-j] != s[i+j]:
                        break
                    if 2*j + 1 > m :
                        m = 2 * j + 1
                        l = i-j
                        r = i+j
    
    
                if i+1 < n and s[i] == s[i+1]:
                    for j in range(min(i+1,n-i-1)):
                        if s[i-j] != s[i+j+1]:
                            break
                        if 2 * j + 2 > m :
                            m = 2*j +2
                            l = i-j
                            r = i+j+1
    
    
            return s[l:r+1]
172
```
K
KEQI HUANG 已提交
173 174


K
KEQI HUANG 已提交
175
思路3:
K
KEQI HUANG 已提交
176

K
KEQI HUANG 已提交
177
[Manacher算法](https://www.felix021.com/blog/read.php?2040) 
178 179 180 181 182 183 184 185 186 187 188 189 190 191

Manacher算法增加两个辅助变量id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。得到一个很重要的结论:

- 如果mx > i,那么P[i] >= Min(P[2 * id - i], mx - i) . 为什么这样说呢,下面解释

下面,令j = 2*id - i,也就是说j是i关于id的对称点。

- 当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于i和j对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有P[i] = P[j];
![](https://github.com/Lisanaaa/myTODOs/blob/master/manacher1.png)

- 当 P[j] >= mx - i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,再具体匹配。
![](https://github.com/Lisanaaa/myTODOs/blob/master/manacher2.png)
所以P[i] >= Min(P[2 * id - i], mx - i),因为以j为中心的绘回文子串的左边界可能会比mx关于id的对称点要大,此时只能证明P[i]=P[2 * id - i]
- 此外,对于 mx <= i 的情况,因为无法对 P[i]做更多的假设,只能让P[i] = 1,然后再去匹配。
192

193 194 195 196
在下面的程序中我的P数组保存的是,以当前字符为回文子串中心时,该回文子串的长度(不包含当前字符自身)


简单地用一个小例子来解释:原字符串为'qacbcaw',一眼就可以看出来最大回文子串是'acbca',
197
下面是我做的图,累shi了!
198

199
![](https://github.com/Lisanaaa/myTODOs/blob/master/manacher3.jpg)
200 201


202

203
所以最终代码中的max_i就是字符'b'所对应的index8,start的值就是(max_i - P[max_i] - 1) / 2 = 1,最终输出结果为s[1:6],即‘acbca’
204

K
KEQI HUANG 已提交
205
```python
K
KEQI HUANG 已提交
206 207 208 209 210 211
class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
K
KEQI HUANG 已提交
212 213
        def preProcess(s):
            if not s:
214
                return ['^', '&']
K
KEQI HUANG 已提交
215
            T = ['^']
216 217
            for i in s:
                T += ['#', i]
K
KEQI HUANG 已提交
218 219 220 221 222
            T += ['#', '$']
            return T
        T = preProcess(s)
        P = [0] * len(T)
        id, mx = 0, 0
223 224
        for i in range(1, len(T)-1):
            j = 2 * id - i
K
KEQI HUANG 已提交
225
            if mx > i:
226
                P[i] = min(P[j], mx-i)
K
KEQI HUANG 已提交
227
            else:
228
                P[i]= 0
K
KEQI HUANG 已提交
229 230
            while T[i+P[i]+1] == T[i-P[i]-1]:
                P[i] += 1
231
            if i + P[i] > mx:
K
KEQI HUANG 已提交
232
                id, mx = i, i + P[i]
233
        max_i = P.index(max(P))    #保存的是当前最大回文子串中心位置的index
K
KEQI HUANG 已提交
234
        start = (max_i - P[max_i] - 1) / 2
235 236
        res = s[start:start+P[max_i]]
        return res
K
KEQI HUANG 已提交
237
```
238
run code的时候结果会跟expected不一样,但是该input确实2个结果都可以,所以放心地submit吧
239
还可以转到[647题](https://github.com/Lisanaaa/thinking_in_lc/blob/master/647._Palindromic_Substrings.md)去看一看,也可以用这个算法解
K
KEQI HUANG 已提交
240 241