题目
读题
- 题目是找出一个字符串串里的最大回文子串
- 回文串就是正着读和反着读都一样的字符串,比如
abcba
- 子串就是字符串中的一部分,且这部分是连续的。区别于子序列。
解题
解题思路
暴力法解题
一开始啥都不用想,使用暴力法解题。主要思路为:
- 找出所有可能的子串
- 遍历子串,判断子串是否是回文字符串,是的记住子串
- 最大长度的回文子串可能由多个,返回任意一个就行了
- 可以使用剪枝算法减少穷举次数
package main
import "fmt"
func getMaxSubCount(s string) string {
length := len(s)
if length <= 1 {
return s
}
var maxSubStr string
// 遍历所有可能的子串
for i := 0; i < length-1; i++ {
// 当前子串长度不大于已知的最大回文子串,则直接跳过
for j := i + len(maxSubStr); j < length; j++ {
if check(s[i : j+1]) {
maxSubStr = s[i : j+1]
}
}
}
return maxSubStr
}
// 判断是否是回文串
func check(s string) bool {
length := len(s)
if length <= 1 {
return true
}
mid := length / 2
for i := 0; i < mid; i++ {
if s[i] != s[length-1-i] {
return false
}
}
return true
}
func main() {
s := "ababa"
fmt.Println(getMaxSubCount(s))
}
动态规划
子串和子序列问题大都与动态规划有关,这里尝试使用动态规划解题。
-
假设已知一个字符串s,并且我们知道它的最大回文子串。
-
现在字符串s右边拼接一个字符c,那么它的最大回文子串会有什么变化呢?
- 举个例子:abacd + d = abacdd,这种情况已知aba是之前的最大子串(长度为3),现在为判断字符d的影响,变成了判断新字符串的后缀子串时候是回文子串。与暴力法没啥区别,遂放弃了。
-
换种角度,驾驶已知长度为a的回文子串,我们要找长度为a+1的子串是不是简单多了,就只需看该子串左右两边的字符是否满足就行了。
package main
import (
"fmt"
)
func getMaxSubCount(s string) string {
length := len(s)
if length <= 1 {
return s
}
var maxSubStr string
// memory[i][j]=1表示字符串s[j:j+i]是回文串
memory := make([][]int, length+1)
for i := 0; i <= length; i++ {
memory[i] = make([]int, length)
}
// 长度<=1子串的都是回文串
maxSubStr = s[:1]
for i := 0; i < length; i++ {
memory[0][i] = 1
memory[1][i] = 1
}
// 逐次寻找长度为i的回文子串
for i := 2; i <= length; i++ {
flag := false
for j := 0; j < length-i+1; j++ {
// 如果s[j+1:(j+1)+(i-2)+1]是个回文串(长度为i-2),且s[j]==s[j+i-1],那么s[j:j+i]也是回文串
if memory[i-2][j+1] == 1 && s[j] == s[j+i-1] {
memory[i][j] = 1
// 记住最大子串
if !flag {
maxSubStr = s[j : j+i]
flag = true
}
}
}
}
return maxSubStr
}
func main() {
s := "babad"
fmt.Println(getMaxSubCount(s))
}