最長回文子串


1. 問題描述

回文串(palindromic string)是指這個字符串無論從左讀還是從右讀,所讀的順序是一樣的;簡而言之,回文串是左右對稱的。所謂最長回文子串問題,是指對於一個給定的母串

abcdedcb

從所有的為回文串的子串a, ded, cdedc, bcdecdb中;找出最長的那一個bcdecdb。但是該如何判斷子串是否回文然后找出最長者呢?正好Leetcode也有一個5. longest-palindromic-substring問題,可以用來做代碼測試。

2. 求解


2016.12.28更新

將原來的C代碼替換為Go 1.7,修復了評論中所指出的動態規划bug。


窮舉法

最容易想到的是窮舉法,窮舉所有子串,找出是回文串的子串,統計出最長的那一個。

// check whether a string is palindromic
func isPalindromic(s string) bool {
	mid := len(s) / 2
	last := len(s) - 1
	for i := 0; i < mid; i++ {
		if s[i] != s[last - i] {
			return false
		}
	}
	return true
}

// longest palindromic substring
func longestPalindrome(s string) string {
	last := len(s) - 1
	longest := string(s[0])
	for i := 0; i < last; i++ {
		for j := i + 1; j <= last; j++ {
			if isPalindromic(s[i:j + 1]) && j + 1 - i > len(longest) {
				longest = s[i:j+1]
			}
		}
	}
	return longest
}
  • 時間復雜度:判斷是否為回文串的復雜度為\(O(n)\)longestPalindrome有雙層for循環,因此總的復雜度為為\(O(n^3)\)
  • 空間復雜度\(O(1)\)

動態規划

窮舉法的時間復雜度過高,接下來我們用DP進行優化。對於母串s,我們用\(c[i, j] = 1\)表示子串s[i..j]為回文子串,那么就有遞推式

\[c[i,j] = \left\{ {\matrix{ c[i+1, j-1] & {if \ s[i] = s[j]} \cr 0 & {if \ s[i] \ne s[j]} \cr } } \right. \]

上述遞推式的含義:當\(s[i] = s[j]\)時,如果s[i+1..j-1]是回文子串,則s[i..j]也是回文子串;如果s[i+1..j-1]不是回文子串(或\(s[i] \ne s[j]\)),則s[i..j]也不是。

特別地,對於這樣的字符串——只包含單個字符、或者兩個字符重復,其均為回文串:

  • c[i,i] = 1
  • c[i,i+1] = 1 if s[i] == s[i+1]
func longestPalindrome(s string) string {
	length := len(s)
	// longest palindromic substring
	longest := string(s[0])
	// 2-D array initialization
	c := make([][]bool, length)
	for i := range c {
		c[i] = make([]bool, length)
	}
	for gap := 0; gap < length; gap++ {
		for i := 0; i < length - gap; i++ {
			j := i + gap
			if s[i] == s[j] && (j - i <= 2 || c[i + 1][j - 1]) {
				c[i][j] = true
				// find the longest
				if j + 1 - i > len(longest) {
					longest = s[i:j + 1]
				}
			}
		}
	}
	return longest
}
  • 時間復雜度:對二維狀態數組\(c\)采取“之”字形賦值,增加gap來擴大子串長度;有兩層for循環,故復雜度為\(O(n^2)\).
  • 空間復雜度:開銷在狀態數組\(c\)上,為\(O(n^2)\).

分治法

回文串是左右對稱的,如果從中心軸開始遍歷,會減少一層循環。思路:依次以母串的每一個字符為中心軸,得到回文串;然后通過比較得到最長的那一個。注意:要考慮到像baccab這樣的特殊子串,可以理解它的中心軸是cc

// given a center, find the longest palindromic substring
func l2rHelper(s string, mid int) string {
	l := mid - 1; r := mid + 1
	length := len(s)
	for r < length && s[r] == s[mid] {
		r++
	}
	for l >= 0 && r < length && s[l] == s[r] {
		l--
		r++
	}
	return s[l+1:r]
}

func longestPalindrome(s string) string {
	length := len(s)
	longest := string(s[0])
	for i := 0; i < length -1; i++  {
		if len(l2rHelper(s, i)) > len(longest) {
			longest = l2rHelper(s, i)
		}
	}
	return longest
}
  • 時間復雜度:對於給定中心,找出最長回文子串的復雜度為\(O(n)\);總的復雜度為\(O(n^2)\).
  • 空間復雜度\(O(1)\).

3. 參考資料

[1] programcreek, Leetcode – Longest Palindromic Substring (Java).
[2] GeeksforGeeks, Longest Palindromic Substring | Set 1.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM