718.最长重复子数组
给两个整数数组 A
和 B
,返回两个数组中公共的、长度最长的子数组的长度。
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3, 2, 1] 。
-
这道题目不能用首尾指针方法的原因在于,尾指针不只是和另一个尾指针匹配,和另一个数组指针区间中的任何一个元素匹配都有可能改变当前最长重复子序列长度,也是这道题目的难点,如果考虑让尾指针和每一个元素遍历就是暴力的思想。
-
滑动指针对齐法。每一次固定两个数组的头,匹配两个数组长度重合的队列,遍历时两个数组的指针只需要同步移动,这也是固定头部对齐的好处,不需要交错匹配。最长重复队列一定会在移动到某个位置时,被一对一同步上下匹配。如果把B作为固定串(相当于隧道),A作为滑动串(火车),那么对齐时一定是从A刚进去隧道到A完全出隧道,长度为O(m+n)。时间复杂度O((m+n) ∗ * ∗min(m,n))
-
动规。dp[i][j]表示以A[i]和B[j]元素为结尾的最长重复子数组的长度。这道题又增加了我们对于dp数组含义设计的理解,一道动规题目一般来说要用dp来解决,那么对dp[i][j]进行状态转移时,肯定是只涉及到对i和j下标附近元素的访问和比较(A[i-1],A[i],A[i+1]),因此我们设计dp数组时,每次维护dp[i][j]时添加的元素肯定只能是A[i]和B[j]。所以回到这道题目,如果我们直接设计dp[i][j]表示长度为i和长度为j数组的最长重复数组长度,是肯定不行的,因为A[i]和B[0]~B[j-1]任意一个元素匹配都有可能改变重复数组长度。
43. 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1和 num2乘积,它们乘积也表示为字符串形式。
输入: num1 = “123”, num2 = “456”
输出: "56088
- 模拟计算机乘法。num1和num2每一位相乘把个位结果加入res[i]时,结果可能会溢出10,要往res[i+1]进位。
322.零钱兑换
给你一个整数数组 coins,表示不同面额的硬币;以及一个整数 amount,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1.你可以认为每种硬币的数量是无限的。
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
-
动规。dp[ i i i]=min dp[ i − c j i-c_j i−cj]+1(j=0…n-1),表示金额为i需要的最小硬币数量等于i减去已知币值需要最小金币数量加1。dp是从小往大进行遍历维护,比较恶心的地方在于dp[i]什么时候赋值为-1,仅当往下找的全部币值 i − c j i-c_j i−cj不越下界时均使得 d p [ i − c j ] = − 1 dp[i-c_j]=-1 dp[i−cj]=−1时,则dp[i]赋值为-1。这里用一个初始化为一个极大值的标志位min来辅助判断,如果min没有被修改,则说明底层要么是i-coin越下界的要么是不能用硬币表示的。
如果写成递归的话要注意保存底层已经判断得出硬币数量的值,因为递归是从上层调用底层结果,反复搜索底层结果会超时。因此要开辟额外空间保存结果。
public int coinChange(int[] coins, int amount) {
int[] dp=new int[amount+1];
for(int i=1;i<dp.length;i++)
{
int min=Integer.MAX_VALUE;
for(int coin:coins)
{
if(i-coin>=0&&dp[i-coin]!=-1)
min=dp[i-coin]<min?dp[i-coin]:min;
}
if(min==Integer.MAX_VALUE) dp[i]=-1;
else dp[i]=min+1;
}
return dp[amount];
}
今日总结
刷题+初一爬山