5. Longest Palindromic Substring
...大约 3 分钟
给你一个字符串
s,找到s中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。示例 2:
输入:s = "cbbd" 输出:"bb"提示:
1 <= s.length <= 1000s仅由数字和英文字母组成
1. 动态规划
1.1 状态转移方程
假设表示字符串S[i:j]是否为回文串, 那么有:
- 若不是回文, 那么不是回文
 - 若是回文, 只有当时, 为回文
 
可以得出:
初始值(最小问题解):
- 当有两个字符时,
 
迭代法计算过程是一个填表的过程, 以abcba 为例:
| i\j | 0 | 1 | 2 | 3 | 4 | 
|---|---|---|---|---|---|
| 0 | N/A | F (1) | F (5) | F (8) | T (10) | 
| 1 | N/A | N/A | F (2) | T (6) | F (9) | 
| 2 | N/A | N/A | N/A | F (3) | F (7) | 
| 3 | N/A | N/A | N/A | N/A | F (4) | 
| 4 | N/A | N/A | N/A | N/A | N/A | 
括号数字代表计算顺序.
1.2 迭代+二维数组
TC: SC:
func longestPalindrome(s string) string {
    n := len(s)
    // 动态规划
    dp := make([][]bool, n)
    for i := range dp {
        dp[i] = make([]bool, n)
    }
    start := 0
    maxLen := 1
    for j := 0; j < n; j++ {
        for i := 0; i < j; i++ {
            if j-i > 2 {
                dp[i][j] = dp[i+1][j-1] && s[i] == s[j]
            } else {
                dp[i][j] = s[i] == s[j]
            }
            if dp[i][j] && j-i+1 > maxLen {
                start = i
                maxLen = j-i+1
            }
        }
    }
    return s[start:start+maxLen]
}
1.3 迭代+一维数组
TC: SC:
由上述的计算过程图表可以看到, 二维数组有一半的空间是闲置的, 并且当j-i>2时dp[i][j]只需要用到dp[i+1][j-1].
因此将二维数组映射到i上, 得到一维数组dp[0,..i,..,n], 从计算顺序可以看出, 计算dp[i]后替换掉原来的值并不会影响下一次计算.
func longestPalindrome(s string) string {
    n := len(s)
    // 动态规划
    dp := make([]bool, n)
    
    start := 0
    maxLen := 1
    for j := 0; j < n; j++ {
        for i := 0; i < j; i++ {
            if j-i > 2 {
                dp[i] = dp[i+1] && s[i] == s[j]
            } else {
                dp[i] = s[i] == s[j]
            }
            if dp[i] && j-i+1 > maxLen {
                maxLen = j-i+1
                start = i
            }
        }
    }
    return s[start: start+maxLen]
}
2. 中心扩展
TC: SC:
对于回文:
- 若长度为奇数, 那么其对称中心只有一个字符
 - 若长度为偶数, 那么其对称中心是两个相同字符
 
那么若从对称中心出发, 向两边扩展:
- 若扩展的两个字符相同, 则扩展后是回文, 则继续扩展.
 - 若扩展的两个字符不同, 则扩展后不是回文, 停止扩展.
 
停止扩展后的回文一定是最长的回文, 那么以字符串的每个字符开始扩展就可找出最长回文串.
func longestPalindrome(s string) string {
    n := len(s)
    start, end := 0, 0
    for i := 0; i < n; i++ {
        le1, ri1 := expandCenter(s, i, i)
        le2, ri2 := expandCenter(s, i, i+1)
        if ri1-le1 > end-start {
            start, end = le1, ri1
        }
        if ri2-le2 > end-start {
            start, end = le2, ri2
        }
    }
    return s[start: end+1]
}
func expandCenter(s string, left, right int) (int, int) {
    for left >=0 && right < len(s) && s[left] == s[right] {
        left -= 1
        right += 1
    }
    return left+1, right-1
}
Reference
 Powered by  Waline  v2.15.2
