题解 | #最长回文子串#
最长回文子串
http://www.nowcoder.com/practice/12e081cd10ee4794a2bd70c7d68f5507
HJ85 最长回文子串 题解
by 限时烟花
1. 抽丝剥茧
这是一道非常基础的问题,也是每一个学习编程的人都回避不了的问题。
对于第一次接触这个问题的学习者,可以对问题作如下理解:
- 回文:即是要求子串本身是对称的;
- 最长:即是要求在所有子串中找到符合要求的。
2. 化繁为简
根据上一部分的分析,我们可以很容易的想到暴力的解法:在每一个位置检查所有的合法的长度的子串是否是对称的。
3. 码上行动
while True:
try:
s = input() # 输入
len_s = len(s)
result = 0 # result用于记录目前最长的子串长度,初始化为0
for n in range(len): # 从第一个字符的位置开始检查
max_len = result + 1 # max_len代表当前检查字符串的长度
while n + max_len <= len: # 代表搜索完以第n+1个字符串开头的所有字符串
if s[n:n + max_len] == s[n:n + max_len][::-1]: # 如果满足是回文字符串
result = max_len # 则此时最大的字符串长度变为max_len
max_len += 1 # 每次增加字符串的长度1
if result != 0:
print(result)
except:
break
4. 心有成算
时间复杂度:由于嵌套了双层的循环,而在判断回文时需要进行逐位的判断,共计max_len次,故时间复杂度为
空间复杂度:由于使用了n长的临时变量来存储输入,故空间复杂度为
5. 另辟蹊径
首先需要明确的是,如果一个字符串是回文字符串,并且长度大于2,那么将其首尾各去掉一个字母后,剩余的部分仍然是回文字符串。
那么根据这样的思路,则使用动态规划的思想我们可以解决这个问题。
使用表示字符串的第到第个字母组成的字符串是否为回文字符串,那么根据上述的性质,我们容易写出以下的状态转移方程:
以上是普通情况下的状态转移方程,对于动态规划问题我们还需要考虑边界条件。 在本道题目中,边界条件是当字符串长度为1和2时。 长度为1的字符串显然是回文字符串;对于长度为2的字符串,则需要两个字符相同,故有:
则可以如下解法:
while True:
try:
s = input()
n = len(s)
if n < 2:
print(n)
max_len = 1
begin = 0
# dp[i][j] 表示 s[i..j] 是否是回文串
dp = [[False] * n for _ in range(n)]
for i in range(n):
dp[i][i] = True
# 递推开始
# 先枚举子串长度
for L in range(2, n + 1):
# 枚举左边界,左边界的上限设置可以宽松一些
for i in range(n):
# 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
j = L + i - 1
# 如果右边界越界,就可以退出当前循环
if j >= n:
break
if s[i] != s[j]:
dp[i][j] = False
else:
if j - i < 3:
dp[i][j] = True
else:
dp[i][j] = dp[i + 1][j - 1]
# 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if dp[i][j] and j - i + 1 > max_len:
max_len = j - i + 1
print(max_len)
except:
break
复杂度分析:
- 时间复杂度:中间有两层循环,故时间复杂度为
- 空间复杂度:由于使用的dp数组,故空间复杂度为