动态规划
动态规划常适用于有重叠子问题和最优子结构性质的问题,它的本质是以空间换取时间。
它的模板步骤如下:
- 确定动态规划状态;
- 写状态转移方程(画状态转移表);
- 考虑初始化条件;
- 考虑输出;
- 考虑对时间,空间复杂度的优化(较难)
按上述步骤进行详细叙述:
- 思考状态
状态的定义,先试着将题目所求的设置为状态;然后思考状态是如何转移的,如果状态转移方程不易得到,尝试修改定义,目的依然是为了方面得到状态转移方程。 - 思考状态转移方程
- 思考初始化
- 思考输出
对于Q1:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
从回文串的定义思考:如果一个字符串的头尾两个字符都不相等,那么这个字符串一定不是回文串;
如果一个字符串的头尾两个字符相等,进一步讨论:如果里面的字串是回文,则整体是回文串;如果里面的子串不是回文串,整体就不是回文串。
头尾字符相等的情况下,里面子串是否为回文串决定了整个子串是否为回文串,因此可以将状态定义为远字符串的一个子串是否为回文子串。
定义状态
dp[i][j]
表示子串s[i...j]
是否为回文子串,这里子串s[i...j]
为闭区间。
思考状态转移方程
动态规划实际上相当于填一张二维表格,由于考虑子串,因此,i
小于等于j
,也就是只需要填表格的右上部分。
边界条件:如果表达式[i+1 , j-1]
不构成区间,即长度严格小于2
,即j-1-(i+1)+1<2
,整理得j-i<3
。
这个结论很显然:当子串s[i...j]
的长度等于2
或者3
的时候,只需要判断一下头尾两个字符是否相等可直接下结论。
如果子串s[i+1...j-1]
只有1个字符,即去掉两头,剩下中间部分只有1个字符,显然是回文;
如果子串s[i+1...j-1]
为空串,那么子串s[i , j]
一定是回文子串。
因此,在s[i] == s[j]
,成立j-i<3
的前提下,直接可以下结论,dp[i][j] = true
,否则才执行状态转移。
** 考虑初始化**
初始化的时候,单个字符一定是回文串,因此把对角线先初始化为true
,即dp[i][j] = true
。
考虑输出
当得到dp[i][j] = true
,就立即记录子串的长度和起始位置。
class Solution(object):
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
n = len(s)
if n <2:
return s
start = 0
max_len = 1
dp = [[False for _ in range(n)] for _ in range(n)]
for i in range(n):
dp[i][i]=True
for j in range(1,n):
for i in range(j):
if s[i] == s[j]:
if j-i+1 <=3:
dp[i][j] = True
else:
dp[i][j] = dp[i+1][j-1]
else:
dp[i][j]=False
if dp[i][j]:
cur_len = j-i+1
if cur_len>max_len:
max_len = cur_len
start = i
return s[start:start+max_len]
对于Q2:给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
定义状态
dp[i][j]
定义为word1中前i
个字符转化成word2中前j
个字符需要的最少步数。
思考状态转移方程
当word1[i] == word2[j]
时,dp[i][j] = dp[i-1][j-1]
。
当word1[i] != word2[j]
时,dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
。
其中,dp[i-1][j-1]
代表替换操作,dp[i-1][j]
表示删除操作,dp[i][j-1]
表示增加操作。
初始化
dp[i][j] = 0
class Solution(object):
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
m = len(word1)
n = len(word2)
# 加了边界条件,多加一行一列
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
for i in range(n+1):
dp[0][i] = i # 对于第一行,每次都是在之前基础上添加一个操作
for j in range(m+1):
dp[j][0] = j #第一列也是
for i in range(1,m+1):
for j in range(1,n+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i][j-1],dp[i-1][j],dp[i-1][j-1])+1
return dp[-1][-1]