赞
踩
判断两个单词是否是变位词
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)
题目描述:有 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。