判断两个单词是否是变位词
s1 = 'abcde' |
s2 = 'acbde' |
test = AnagramDetection()
方法一:
class AnagramDetection:
# 先对两个字符串进行list化
# 对字符串对应的两个list进行排序
# 依次比较字符是否匹配
def anagramSolution1(self, s1, s2):
alist1 = list(s1)
alist2 = list(s2)
alist1.sort()
alist2.sort()
pos = 0
matches = True
while pos < len(s1) and matches:
if alist1[pos] == alist2[pos]:
pos = pos + 1
else:
matches = False
return matches
方法二:
# 首先将两个字符串list化
# 将两个list中的字符生成两个set
# 比较两个set, 如果不相等直接返回false
# 如果两个set相等, 比较每个set中字符在相应list中的个数, 个数不同返回false
def anagramSolution3(self, s1, s2):
alist1 = list(s1)
alist2 = list(s2)
aset1 = set(alist1)
aset2 = set(alist2)
if aset1 != aset2:
return False
else:
for ch in aset1:
if alist1.count(ch) != alist2.count(ch):
return False
return True
把若干单词按照变位词进行分类:
def string_to_sort_list(word):
l1 = list(word)
l1.sort()
return ''.join(l1)
def AnagramDetection(word_list):
temp_dict = dict()
for word in word_list:
new_word = string_to_sort_list(word)
temp_dict.setdefault(new_word, [])
temp_dict[new_word].append(word)
ret = []
for key, value in temp_dict.iteritems():
ret.append(value)
print ret
word_list = ["cars", "thing", "scar", "dog", "god", "arcs", "the"]
AnagramDetection(word_list)
#print [['cars', 'scar', 'arcs'], ['the'], ['thing'], ['dog', 'god']]
1、硬币最大值问题
# 解决动态规划中的币值最大化问题---在满足所选硬币不相邻的条件下,从一堆硬币中选择最大金额的硬币
# 输入数组C[1..n]保存n个硬币的面值
# 输出可选硬币的最大金额
def coinRow(coinrow):
alist = [0]*(len(coinrow)+1)
alist[1] = coinrow[0]
for i in range(2, len(coinrow)+1):
alist[i] = max(coinrow[i-1]+alist[i-2], alist[i-1])
return alist.pop()
print(coinRow([5, 1, 2, 10, 6, 2]))
private int rob(int[] nums, int first, int last) {
int pre2 = 0, pre1 = 0;
for (int i = first; i <= last; i++) {
int cur = Math.max(pre1, pre2 + nums[i]);
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
2、0-1背包问题:
参照博文:https://blog.csdn.net/wulitaotao96/article/details/95097133
w= [0 , 2 , 3 , 4 , 5 ] #商品的体积2、3、4、5
v = [0 , 3 , 4 , 5 , 6 ] #商品的价值3、4、5、6
bagV = 8; #背包大小
dp = [[0 for i in range(9) ]for i in range(5)] #动态规划表
item = [0]*5 #最优解情况
def findMax(): #动态规划
for i in range (5):
for j in range(bagV+1):
if j < w[i]:
dp[i][j] = dp[i - 1][j];
else:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
def findWhat(i,j): #最优解情况
if i>=0:
if dp[i][j] == dp[i-1][j]:
item[i] = 0
findWhat(i-1,j)
elif j - w[i] >= 0 and dp[i][j] == dp[i - 1][j - w[i]] + v[i]:
item[i] = 1
findWhat(i-1,j-w[i])
def print1():
for i in range(5):
res = []
for j in range(9):
res.append(dp[i][j])
print(res)
print(item)
findMax()
findWhat(4,8)
print1()
3、斐波那契数列
爬楼梯
70. Climbing Stairs (Easy)
Leetcode / 力扣
题目描述:有 N 阶楼梯,每次可以上一阶或者两阶,求有多少种上楼梯的方法。
定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。
第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。
考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。
public int climbStairs(int n) {
if (n <= 2) {
return n;
}
int pre2 = 1, pre1 = 2;
for (int i = 2; i < n; i++) {
int cur = pre1 + pre2;
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
4、矩阵的最小路径和
参照:https://blog.csdn.net/FiveStarsGeneral/article/details/90941097
(1)leetcode 64题
题目描述:
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
Solution:
又涉及到最小路径,或是求最优解的题目,基本上就套动态规划...就要考虑状态转移方程:
设最短路径为dp[][]这道题目的状态转移方程存在这么几种情况:
如果i=j=0,即当前位置为左上角:
dp[i][j] = grid[0][0]
2.如果i=0,j!=0,即当前位置在第一行:
dp[i][j] = grid[i][j] + dp[i][j-1]
因为上一个位置只能是左面的位置
3.如果j=0,i!=0,即当前位置在第一列:
dp[i][j] = grid[i][j] + dp[i-1][j]
因为上一个位置只能是上面的位置
4.其他情况,即在除第一行第一列其他位置:
dp[i][j] = grid[i][j] + min(dp[i][j-1],dp[i-1][j])
这就要看左面位置和上面位置哪个数小
CODE:
v = np.array([[1, 3, 5, 9], [8, 1, 3, 4], [5, 0, 6, 1], [8, 8, 4, 0]])
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
:m 行数
:n 列数
"""
m,n = len(grid),len(grid[0])
dp = [[0 for i in range(n)] for j in range(m)]
for i in range(m):
for j in range(n):
if i == 0 and j == 0:
dp[0][0] = grid[0][0]
elif i == 0:
dp[i][j] = grid[i][j] + dp[i][j - 1]
elif j == 0:
dp[i][j] = grid[i][j] + dp[i - 1][j]
else:
dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1])
return dp[m-1][n-1]
mt = Solution()
print(mt.minPathSum(v))
当然也可以不使用额外的数组存储路径值,直接用grid自身就可以。用一个dp二维数组可以更清晰一些
(2)leetcode 62题
题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
res=[[1 for i in range(n)] for i in range(m)]
#m与n的位置别弄反了,m是行,n是列,第一行,第一列初始值都赋值为1
for i in range(1,m):
for j in range(1,n):
res[i][j]=res[i-1][j]+res[i][j-1]
return res[m-1][n-1]
(3)编辑距离
链接:https://www.zhihu.com/question/23995189/answer/1094101149
这次给的这道题比上面的难一些,在 leetcdoe 的定位是 hard 级别。好像是 leetcode 的第 72 号题。
问题描述
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符 删除一个字符 替换一个字符
示例:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
解答
还是老样子,按照上面三个步骤来,并且我这里可以告诉你,90% 的字符串问题都可以用动态规划解决,并且90%是采用二维数组。
步骤一、定义数组元素的含义
由于我们的目的求将 word1 转换成 word2 所使用的最少操作数 。那我们就定义 dp[i] [j]的含义为:当字符串 word1 的长度为 i,字符串 word2 的长度为 j 时,将 word1 转化为 word2 所使用的最少操作次数为 dp[i] [j]。
有时候,数组的含义并不容易找,所以还是那句话,我给你们一个套路,剩下的还得看你们去领悟。
步骤二:找出关系数组元素间的关系式
接下来我们就要找 dp[i] [j] 元素之间的关系了,比起其他题,这道题相对比较难找一点,但是,不管多难找,大部分情况下,dp[i] [j] 和 dp[i-1] [j]、dp[i] [j-1]、dp[i-1] [j-1] 肯定存在某种关系。因为我们的目标就是,**从规模小的,通过一些操作,推导出规模大的。对于这道题,我们可以对 word1 进行三种操作
插入一个字符 删除一个字符 替换一个字符
由于我们是要让操作的次数最小,所以我们要寻找最佳操作。那么有如下关系式:
一、如果我们 word1[i] 与 word2 [j] 相等,这个时候不需要进行任何操作,显然有 dp[i] [j] = dp[i-1] [j-1]。(别忘了 dp[i] [j] 的含义哈)。
二、如果我们 word1[i] 与 word2 [j] 不相等,这个时候我们就必须进行调整,而调整的操作有 3 种,我们要选择一种。三种操作对应的关系试如下(注意字符串与字符的区别):
(1)、如果把字符 word1[i] 替换成与 word2[j] 相等,则有 dp[i] [j] = dp[i-1] [j-1] + 1;
(2)、如果在字符串 word1末尾插入一个与 word2[j] 相等的字符,则有 dp[i] [j] = dp[i] [j-1] + 1;
(3)、如果把字符 word1[i] 删除,则有 dp[i] [j] = dp[i-1] [j] + 1;
那么我们应该选择一种操作,使得 dp[i] [j] 的值最小,显然有
dp[i] [j] = min(dp[i-1] [j-1],dp[i] [j-1],dp[[i-1] [j]]) + 1;
于是,我们的关系式就推出来了,
步骤三、找出初始值
显然,当 dp[i] [j] 中,如果 i 或者 j 有一个为 0,那么还能使用关系式吗?答是不能的,因为这个时候把 i - 1 或者 j - 1,就变成负数了,数组就会出问题了,所以我们的初始值是计算出所有的 dp[0] [0….n] 和所有的 dp[0….m] [0]。这个还是非常容易计算的,因为当有一个字符串的长度为 0 时,转化为另外一个字符串,那就只能一直进行插入或者删除操作了。
public int minDistance(String word1, String word2) {
int n1 = word1.length();
int n2 = word2.length();
int[][] dp = new int[n1 + 1][n2 + 1];
// dp[0][0...n2]的初始值
for (int j = 1; j <= n2; j++)
dp[0][j] = dp[0][j - 1] + 1;
// dp[0...n1][0] 的初始值
for (int i = 1; i <= n1; i++) dp[i][0] = dp[i - 1][0] + 1;
// 通过公式推出 dp[n1][n2]
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2; j++) {
// 如果 word1[i] 与 word2[j] 相等。第 i 个字符对应下标是 i-1
if (word1.charAt(i - 1) == word2.charAt(j - 1)){
p[i][j] = dp[i - 1][j - 1];
}else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
}
}
}
return dp[n1][n2];
}
5、数组区间
(1)leetcode 303题
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
示例:
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
说明:
你可以假设数组不可变。
会多次调用 sumRange 方法。
Solution:(由于当array很长时,使用切片的方法求和会导致时间超时。所以可以新建一个array,它的每个元素代表对应前n个元素的和,例如nums_dp[1]表示数组nums前1个元素的和。对于给定区间[2, 5]对应的就是前6个元素的和减去前2个元素的和)
class NumArray(object):
def __init__(self, nums):
"""
:type nums: List[int]
"""
self.nums_dp = [0] * (len(nums)+1)
for i in range(1, len(nums)+1):
self.nums_dp[i] = self.nums_dp[i-1] + nums[i-1]
def sumRange(self, i, j):
"""
:type i: int
:type j: int
:rtype: int
"""
return self.nums_dp[j+1] - self.nums_dp[i]
solution = NumArray([-2,0,3,-5,2,-1])
print(solution.sumRange(0, 2), solution.sumRange(2, 5), solution.sumRange(0, 5))
#输出 1 -1 -3