演算法與資料結構
  • 簡介
  • 前言
    • 事前準備
    • 資料結構場景
    • 複雜度分析
      • 時間複雜度
      • 空間複雜度
  • 分類題型
    • Array 陣列
      • 88. Merge Sorted Array
      • 1089. Duplicate Zeros
      • 941. Valid Mountain Array
      • 1710. Maximum Units on a Truck
      • 54. Spiral Matrix
      • 73. Set Matrix Zeroes
      • 498. Diagonal Traverse
      • 238. Product of Array Except Self
      • HackerRank Counting Valleys
      • 1089. Duplicate Zeros
    • Backtrack 回溯法
      • 51. & 52. N Queens
      • 37. Sudoku Solver
      • 77. Combinations
      • 39. Combination Sum
        • 40. Combination Sum II
        • 216. Combination Sum III
      • 78. Subsets
        • 90. Subsets II
      • 46. Permutations
        • 47. Permutations II
      • 22. Generate Parentheses
      • 1087. Brace Expansion
      • 332. Reconstruct Itinerary
      • 489. Robot Room Cleaner
      • 17. Letter Combinations of a Phone Number
      • 79. Word Search
        • 212. Word Search II
      • 425. Word Squares
      • 1219. Path with Maximum Gold
      • 247. Strobogrammatic Number II
    • Binary Search 二分搜索
      • Rotated Array 旋轉陣列問題
        • 33. Search in Rotated Sorted Array
        • 81. Search in Rotated Sorted Array II
        • 153. Find Minimum in Rotated Sorted Array
        • 154. Find Minimum in Rotated Sorted Array II
      • 374. Guess Number Higher or Lower
      • 704. Binary Search
      • 34. Find First and Last Position of Element in Sorted Array
      • 69. Sqrt(x)
      • 367. Valid Perfect Square
      • 374. Guess Number Higher or Lower
      • 278. First Bad Version
      • 162. Find Peak Element
      • 852. Peak Index in a Mountain Array
      • 35. Search Insert Position
      • 875. Koko Eating Bananas
      • 1011. Capacity To Ship Packages Within D Days
      • 173. Binary Search Tree Iterator
      • 1586. Binary Search Tree Iterator II
    • Dynamic Programming 動態規劃
      • 509. Fibonacci Number
      • 70. Climbing Stairs
      • 55. Jump Game
      • 62. Unique Paths
      • 64. Minimum Path Sum
      • 174. Dungeon Game
      • 91. Decode Ways
      • 72. Edit Distance
      • 221. Maximal Square
      • 329. Longest Increasing Path in a Matrix
      • 198. House Robber
        • 213. House Robber II
      • 1109. Corporate Flight Bookings
      • 983. Minimum Cost For Tickets
      • 1143. Longest Common Subsequence
        • 583. Delete Operation for Two Strings
        • 712. Minimum ASCII Delete Sum for Two Strings
      • 53. Maximum Subarray
      • 152. Maximum Product Subarray
      • 975. Odd Even Jump
      • 115. Distinct Subsequences
      • 416. Partition Equal Subset Sum
      • 10. Regular Expression Matching
      • 651. 4 Keys Keyboard
    • Hash Table/Set 雜湊表
      • 242. Valid Anagram
      • 49. Group Anagrams
      • 217. Contains Duplicate
        • 219. Contains Duplicate II
        • 220. Contains Duplicate III
      • 187. Repeated DNA Sequences
      • 1170. Compare Strings by Frequency of the Smallest Character
      • 448. Find All Numbers Disappeared in an Array
      • 560. Subarray Sum Equals K
      • 1010. Pairs of Songs With Total Durations Divisible by 60
    • Heap 堆
      • 347. Top K Frequent Elements
      • 692. Top K Frequent Words
      • 973. K Closest Points to Origin
      • 128. Longest Consecutive Sequence
      • 1167. Minimum Cost to Connect Sticks
    • Linked List 鏈結串列
      • 876. Middle of the Linked List
      • 21. Merge Two Sorted Lists
        • 23. Merge k Sorted Lists
      • 148. Sort List
      • 206. Reverse Linked List
        • 92. Reverse Linked List II
    • Stack 棧
      • 20. Valid Parentheses
      • 394. Decode String
      • 84. Largest Rectangle in Histogram
      • 155. Min Stack
    • String 字串
      • 43. Multiply Strings
      • 344. Reverse String
      • 726. Number of Atoms
      • 8. String to Integer (atoi)
      • 12. Integer to Roman
      • 696. Count Binary Substrings
    • Tree 樹
      • Breadth-first search 廣度優先搜索
        • 111. Minimum Depth of Binary Tree
        • 200. Number of Islands
        • 752. Open the Lock
        • 279. Perfect Squares
        • 286. Walls and Gates
        • 417. Pacific Atlantic Water Flow
        • 994. Rotting Oranges
        • 429. N-ary Tree Level Order Traversal
        • 116. Populating Next Right Pointers in Each Node
        • 117. Populating Next Right Pointers in Each Node II
        • 430. Flatten a Multilevel Doubly Linked List
        • 1135. Connecting Cities With Minimum Cost
      • Preorder 前序遍歷
        • 105. Construct Binary Tree from Preorder and Inorder Traversal
        • 144. Binary Tree Preorder Traversal
        • 589. N-ary Tree Preorder Traversal
        • 255. Verify Preorder Sequence in Binary Search Tree
      • Inorder 中序遍歷
        • 94. Binary Tree Inorder Traversal
        • 426. Convert Binary Search Tree to Sorted Doubly Linked List
      • Postorder 後序遍歷
        • 106. Construct Binary Tree from Inorder and Postorder Traversal
        • 145. Binary Tree Postorder Traversal
        • 590. N-ary Tree Postorder Traversal
        • 114. Flatten Binary Tree to Linked List
        • 652. Find Duplicate Subtrees
        • 124. Binary Tree Maximum Path Sum
        • 543. Diameter of Binary Tree
        • 337. House Robber III
      • BST
        • 98. Validate Binary Search Tree
        • 450. Delete Node in a BST
        • 700. Search in a Binary Search Tree
        • 701. Insert into a Binary Search Tree
        • 1373. Maximum Sum BST in Binary Tree
        • 230. Kth Smallest Element in a BST
        • 99. Recover Binary Search Tree
      • Serialization & Deserialization
        • 606. Construct String from Binary Tree
        • 536. Construct Binary Tree from String
        • 297. Serialize and Deserialize Binary Tree
        • 428. Serialize and Deserialize N-ary Tree
      • Graph 圖
        • 1971. Find if Path Exists in Graph
        • 323. Number of Connected Components in an Undirected Graph
        • 547. Number of Provinces
      • 100. Same Tree
        • 572. Subtree of Another Tree
      • 1379. Find a Corresponding Node of a Binary Tree in a Clone of That Tree
      • 226. Invert Binary Tree
      • 104. Maximum Depth of Binary Tree
      • 559. Maximum Depth of N-ary Tree
      • 102. Binary Tree Level Order Traversal
      • 261. Graph Valid Tree
      • 250. Count Univalue Subtrees
      • 222. Count Complete Tree Nodes
      • 112. Path Sum
      • 113. Path Sum II
      • 437. Path Sum III
    • Trie 字典樹
      • 208. Implement Trie (Prefix Tree)
      • 677. Map Sum Pairs
      • 648. Replace Words
      • 588. Design In-Memory File System
      • 642. Design Search Autocomplete System
      • 211. Design Add and Search Words Data Structure
      • 1268. Search Suggestions System
    • Two Pointers 雙指針
      • 977. Squares of a Sorted Array
      • 1095. Find in Mountain Array
      • 27. Remove Element
      • 141. Linked List Cycle
        • 142. Linked List Cycle II
      • 19. Remove Nth Node From End of List
      • 26. Remove Duplicates from Sorted Array
      • 83. Remove Duplicates from Sorted List
      • 283. Move Zeroes
    • Sliding Window 滑動窗口
      • 3. Longest Substring Without Repeating Characters
      • 76. Minimum Window Substring
      • 567. Permutation in String
      • 438. Find All Anagrams in a String
      • 424. Longest Repeating Character Replacement
      • 485. Max Consecutive Ones
      • 1004. Max Consecutive Ones III
      • 904. Fruit Into Baskets
      • 1248. Count Number of Nice Subarrays
      • 1358. Number of Substrings Containing All Three Characters
      • 1234. Replace the Substring for Balanced String
      • 930. Binary Subarrays With Sum
      • 209. Minimum Size Subarray Sum
      • 992. Subarrays with K Different Integers
      • 713. Subarray Product Less Than K
      • 862. Shortest Subarray with Sum at Least K
      • 239. Sliding Window Maximum
      • 159. Longest Substring with At Most Two Distinct Characters
      • 340. Longest Substring with At Most K Distinct Character
      • 992. Subarrays with K Different Integers
    • Bit Manipulation 位元運算
      • 136. Single Number
      • 7. Reverse Integer
      • 191. Number of 1 Bits
    • Math 數學
      • 553. Optimal Division
      • 204. Count Primes
      • 372. Super Pow
      • 829. Consecutive Numbers Sum
      • 1492. The kth Factor of n
    • Other 其他
      • 31. Next Permutation
      • 1446. Consecutive Characters
      • 386. Lexicographical Numbers
      • 269. Alien Dictionary
      • 48. Rotate Image
      • 157. Read N Characters Given Read4
        • 158. Read N Characters Given Read4 II - Call multiple times
      • 246. Strobogrammatic Number
    • Object Oriented Design 物件導向設計
      • 710. Random Pick with Blacklist
      • 380. Insert Delete GetRandom O(1)
      • 271. Encode and Decode Strings
      • 348. Design Tic-Tac-Toe
  • 經典題目
    • Best Time to Buy and Sell Stock 股票買賣問題
      • 121. Best Time to Buy and Sell Stock
      • 122. Best Time to Buy and Sell Stock II
      • 123. Best Time to Buy and Sell Stock III
      • 188. Best Time to Buy and Sell Stock IV
      • 309. Best Time to Buy and Sell Stock with Cool down
      • 714. Best Time to Buy and Sell Stock with Transaction Fee
    • Palindrome 回文
      • 125. Valid Palindrome
      • 680. Valid Palindrome II
      • 266. Palindrome Permutation
      • 9. Palindrome Number
      • 866. Prime Palindrome
      • 5. Longest Palindromic Substring
      • 647. Palindromic Substrings
      • 516. Longest Palindromic Subsequence
      • 1930. Unique Length-3 Palindromic Subsequences
      • 234. Palindrome Linked List
    • Time Intervals 時間區間問題
      • 252. Meeting Rooms
      • 253. Meeting Rooms II
      • 56. Merge Intervals
      • 57. Insert Interval
      • 495. Teemo Attacking
      • 759. Employee Free Time
      • 986. Interval List Intersections
      • 435. Non-overlapping Intervals
      • 452. Minimum Number of Arrows to Burst Balloons
      • 729. My Calendar I
      • 731. My Calendar II
      • 732. My Calendar III
      • 163. Missing Ranges
      • 1024. Video Stitching
    • Calculator 計算機問題
      • 224. Basic Calculator
      • 227. Basic Calculator II
      • 772. Basic Calculator III
    • Add One 加一問題
      • 66. Plus One
      • 67. Add Binary
      • 369. Plus One Linked List
      • 2. Add Two Numbers
        • 445. Add Two Numbers II
      • 989. Add to Array-Form of Integer
    • Clone Graph 複製圖形
      • 133. Clone Graph
      • 1490. Clone N-ary Tree
      • 138. Copy List with Random Pointer
      • 1485. Clone Binary Tree With Random Pointer
    • Cache 快取問題
      • 146. LRU Cache 最久未使用演算法
      • 460. LFU Cache 最近最少使用演算法
    • n Sum 問題
      • 1. 2 Sum
      • 15. 3Sum
      • 18. 4Sum
      • 454. 4 Sum II
      • 167. Two Sum II - Input array is sorted
      • 170. Two Sum III - Data structure design
      • 653. Two Sum IV - Input is a BST
      • 16. 3Sum Closest
      • 259. 3Sum Smaller
      • 16. 3Sum Closest
    • Lowest Common Ancestor of a Binary Tree 最近共同祖先問題
    • The Maze 球滾迷宮問題
      • 490. The Maze
      • 505. The Maze II
    • Find Median 尋找中位數
      • 295. Find Median from Data Stream
      • 4. Median of Two Sorted Arrays
    • Course 課程問題
      • 207. 210. Course Schedule I & II
      • 1136. Parallel Courses
    • Coin Change 零錢問題
      • 322. Coin Change
      • 518. Coin Change 2
    • Binary Indexed Tree 樹狀陣列或二元索引樹
      • 303. Range Sum Query - Immutable
      • 307. Range Sum Query - Mutable
      • 315. Count of Smaller Numbers After Self
    • Longest Increasing Subsequence 最長遞增子序列的問題
      • 300. Longest Increasing Subsequence
      • 354. Russian Doll Envelopes
      • 673. Number of Longest Increasing Subsequence
    • Robot Bounded In Circle 掃地機器人
    • Containing Water 裝水問題
      • 11. Container With Most Water
      • 42. Trapping Rain Water
      • 755. Pour Water
    • Word Ladder 文字梯問題
      • 127. Word Ladder
      • 126. Word Ladder II
    • Egg Drop 高樓扔雞蛋
    • Custom sorting 排序技巧
      • 937. Reorder Data in Log Files
    • Word Break 字串組合問題
      • 139. Word Break
      • 140. Word Break II
      • 472. Concatenated Words
  • 常見演算法
    • Sorting 排序
      • Merge Sort (Accepted)
      • Quick Sort (Accepted)
      • Heap Sort (Accepted)
      • Bubble Sort (TLE)
      • Insertion Sort (TLE)
      • Selection Sort (TLE)
    • Shuffle Array 打亂陣列內的元素
    • 池塘抽樣
      • 382. Linked List Random Node
      • 398. Random Pick Index
  • Python 技巧
    • 陣列複製
    • 矩陣操作
      • 向矩陣中的四個方向移動
      • 矩陣遍歷的方法
Powered by GitBook
On this page
  • 暴力法 (TLE)
  • 動態規劃
  • 中心擴散法
  1. 經典題目
  2. Palindrome 回文

5. Longest Palindromic Substring

Previous866. Prime PalindromeNext647. Palindromic Substrings

Last updated 2 years ago

這一題是我們要找出子字串中的最長的回文,其實最好從暴力解法開始學習起,前面的題目已經重複提過了好多次的回文的性質,這裡就用暴力法先來思考以下。

暴力法 (TLE)

那暴力法的方式就是我們窮舉出所有的子字串,並去看看哪一個字串是回文,而且長度最長就好了,窮舉的方式如下,直接就先固定左標,滑動右標一個一個把子字串窮舉出來。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        substrings = []
        # i 不用走到最後,因為走到最後的話就等於沒有字元了        
        for i in range(len(s)-1):
            # j 直接從 i 的下一個字元開始看起即可,長度為一個字元不用考慮。
            for j in range(i+1, len(s)):
                substrings.append(s[i:j+1])

這裡也先回憶回文的特性,其中之一就是最短的回文就是只有一個字元,所以我們今天要找的一定是要看看有沒有大於一個字元以上的回文,如果沒有的話,從字串裡面隨便選一個字元就是答案了,這就是預設的答案,緊接著用 的方法,求出最長的回文子字串。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        substrings = []        
        for i in range(len(s)-1):
            for j in range(i+1, len(s)):
                substrings.append(s[i:j+1])

        def isPalindrome(substring):
            left = 0
            right = len(substring) - 1
            while left < right:
                if substring[left] != substring[right]:
                    return False
                left += 1
                right -= 1
            return True

        ans = s[0]
        for substring in substrings:
            if isPalindrome(substring):
                if len(substring) > len(ans):
                    ans = substring
        return ans

不過其實這道題目我們也不一定要一直更新字串,只要知道索引就好,為什麼 length 是用 j - i + 1 是因為,我們需要的答案是閉區間。

class Solution:
    def longestPalindrome(self, s: str) -> str:

        def isPalindrome(left, right):
            while left < right:
                if s[left] != s[right]:
                    return False
                left += 1
                right -= 1
            return True

        start = 0
        length = 1
        for i in range(len(s)-1):
            for j in range(i+1, len(s)):
                if isPalindrome(left, right) and j - i + 1 > length:
                    length = j - i + 1
                    start = i

        return s[start:start+length]

如果是第一次寫這個題目,可以想到這個方法其實不差,不過這個解法因為他的時間複雜度是 O(n3)O(n^3)O(n3) ,在面試的時候應該會繼續被問到,是不是可以提供 O(n2)O(n^2)O(n2) 的方法?要想到就會比較難了。

動態規劃

動態規劃的核心思想是要判斷字串從 i 到 j 是不是一組回文,如果說 s[i] 和 s[j] 相等,又知道 s[i-1..j-1] 已經是回文的話,那就可以確定 s[i..j] 是回文,也就是說子問題是判斷更小的字串是不是回文,是的話,那只要更大的問題滿足條件像是 s[i] 和 s[j] 相等,那就可以回答 s[i..j] 是回文。(這裡的表示法跟 Python 不同,因為 Python 是左閉右開,用 Python 的表達方式會怪怪的。)

所以這題可以用動態規劃的方式來思考

dp[i][j] 代表的是字串 s 以索引 i 開始,到 j 結束的這個閉區間內,是不是回文?

狀態轉移方程式,頭尾要是一樣的字,且中間字串是回文

dp[i][j] = s[i] == s[j] and dp[i+1][j-1]

既然是處理字串的位置,所以就有邊界的情況要處理。

今天我們判斷的首尾兩個字元,又是回到了我們講的回文兩種型態,一種是回文是奇數個數,一種是回文是偶數個數。

如果今天奇數個數在判斷首尾的時候,剛好是在正中心的點的左右兩側,那今天 dp[i+1][j-1] 其實會是 False ,我們要避免這種情況,所以如果今天 j-1 - (i+1) + 1 <= 1 的情況,那 dp[i][j] 應該要是 True ,偶數其實亦然。這就是為何 L17 要做這樣的判斷。

class Solution:
    def longestPalindrome(self, s: str) -> str:

        n = len(s)

        dp = [[False for _ in range(n)] for _ in range(n)]

        for i in range(n):
            dp[i][i] == True

        start = 0
        maxLen = 1

        for end in range(1, n):
            for begin in range(end):
                if s[begin] != s[end]:
                    dp[begin][end] = False
                else:
                    if (end - 1 - (begin + 1) + 1 <= 1):
                        dp[begin][end] = True
                    else:
                        dp[begin][end] = dp[begin+1][end-1]
                if dp[begin][end] and end - begin + 1 > maxLen:
                    maxLen = end-begin + 1
                    start = begin
        
        return s[start:start+maxLen]

下面是上面的程式碼整理過後的結果,但是閱讀性比較差,建議理解上方的就好,但是能夠完整了表達出動態轉移方程

dp[begin][end] = s[begin] == s[end] and (dp[begin+1][end-1] or (end-begin) <= 2))
class Solution:
    def longestPalindrome(self, s: str) -> str:

        n = len(s)

        dp = [[False for _ in range(n)] for _ in range(n)]

        for i in range(n):
            dp[i][i] == True
        start = 0
        maxLen = 1
        for end in range(1, n):
            for begin in range(end):
                dp[begin][end] = s[begin] == s[end] and (dp[begin+1][end-1] or (end-begin) <= 2)
                if dp[begin][end] and end - begin + 1 > maxLen:
                    maxLen = end-begin + 1
                    start = begin
        return s[start:start+maxLen]

但是動態規劃的時間複雜度是 O(n2)O(n^2)O(n2) ,我在第一次寫的時候 LeetCode 是可以通過的,但是後來複習的時候,這個方法超時了。

中心擴散法

下一個方法是利用回文的另一個特性,那就是回文除了從兩側找,也可以從中心找,從中心判斷是不是回文的情況,其複雜度也是只有 O(n)O(n)O(n) ,而如果我們把每個字元都當作中心點擴散,遍歷完所有的中心點也就是遍歷了字串中的所有字元,那時間複雜度也可以在 O(n)O(n)O(n) 。最壞的時間複雜度就是在 O(n2)O(n^2)O(n2)。

這個題目的中心擴散法要求的,是我們要看從中心開始展開的時候,可以展開到多少的長度,我們就先看這個部分:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        def getPalindromeLen(left, right):
            while left >= 0 and right < len(s):
                if s[left] != s[right]:
                    break    
                left -= 1
                right += 1

            return right - left + 1 - 2

這裡有第二的地方要注意,我沒有把它簡化,那就是最後一個回傳值為什麼是這麼奇怪的 right - left + 1 - 2 減二的原因是因為在 while 迴圈裡面,我們是兩個指針分別向外個走一步後,才發現不是回文的,所以多走了兩部要剪掉。如果還是不好想,可以試著用回文的特性想看看。

  1. 奇數的 abc ,傳入 left = 1, right = 1 時,while 迴圈的 if 條件不會觸發,所以指針會向左向右個一步,變成 left = 0, right = 2 ,這時候我們要回傳的回文長度應該是 1 才對,把變數帶入:(2 - 0) + 1 - 2 = 1 正確。

  2. 偶數的 abab 傳入 left = 1, right = 2 時,while 迴圈裡的 if 條件會觸發,所以指針不動,依然是left = 1, right = 2 這時候回文的長度應該是 0 ,把變數帶入:(2 - 1) + 1 - 2 = 0 正確。

class Solution:
    def longestPalindrome(self, s: str) -> str:
        def getPalindromeLen(left, right):
            while left >= 0 and right < len(s):
                if s[left] != s[right]:
                    break    
                left -= 1
                right += 1

            return right - left + 1 - 2

        start = 0
        max_length = 1
        for i in range(len(s)):
            odd = getPalindromeLen(i, i)
            even= getPalindromeLen(i, i+1)
            curr = max(odd, even)
            if curr > max_length:
                max_length = curr
                start = i - (max_length - 1)//2

        return s[start:start+max_length]

最後,我們第一個字元開始掃描到最後一個字元,至於 for 最終的邊界是 len(s) 或是 len(s)-1 其實沒關係,因為如果真的越界了,getPalindromeLen 就有處理了越界,不會造成錯誤。謹慎一點應該要寫 len(s)-1 這裡只是作為註記所以寫了 len(s) 。

至於為何會有 odd 和 even ,寫到這裡已經覆述了好多次回文的特性,因為我們擴展時,要同時考兩回文是奇數的型態,或是偶數的型態,然後我們看哪種型態的回文長度比較長,我們就更新比較長的回文長度。

走到這裡,是這題的的最後一個難點了,那就是我們從中間點出發了,我們要怎麼透過長度與中間點找回出發點,很簡單,既然我們在中間點,那就從中間點減去長度的一半就好,那為什麼會需要減一?一樣又是回文的奇數與偶數的情況。

例如:找到的最長回文字串時,出發的中點是 7 ,如果最大長度是 5 ,那回文字串是奇數,那回文的索引範圍是:5, 6, 7, 8, 9, 起始點是 5 ,7 - (5-1)//2 = 5 正確。如果最大長度是 4 ,是偶數,那回文的索引範圍是:6, 7, 8, 9 ,起始點是 6 ,7-(4-1)//2 = 6 正確。這個減一在程式裡的除法中剛好會讓奇數少 1 偶數少 2 ,剛好是中間點的數量個數。

但是使用中心擴散法,就要注意在 裡面中心擴散法要注意的是要判斷回文的長度是奇數還是偶數。

首先傳進來的座標點是中心點,中心點會有分 left 和 right ,是因為回文的特性,回文有分奇數長度和偶數長度,就像在 的中心擴散法一樣,我們針對偶數求中心點時,向右行的指針要加一。

5. Longest Palindromic Substring
125. Valid Palindrome
125. Valid Palindrome
125. Valid Palindrome