文章目录
- 1658. 将 x 减到 0 的最小操作数
- 题干:
- 算法原理:滑动窗口
- 代码:
- 91. 解码方法
- 题干:
- 算法原理:
- 1、状态表示
- 2、状态转移方程
- 3、初始化
- 4、填表顺序
- 5、返回值
- 6、代码:
- 7、优化
1658. 将 x 减到 0 的最小操作数
原题链接
题干:
一个整数数组 nums
一个整数 x
移除整数数组最左或最右的值,x减去这个值
一直如此,x = 0 就返回最小操作数,要不然就返回 -1
算法原理:滑动窗口
在上面的题干解析中,我们通过图可以发现,我们要找的是左边加右边等于 x
这样我们如果设整个数组的和为 sum
减去的为 x,那么中间的定义为 target = sum - x
这个时候,我们可以直接找中间区间等于 target 的最长区间
转化为:找出最长子数组的长度,所有元素的和正好等于 sum-x
由于left 到 right 之间的值肯定 >= target
这个时候,我们就可以让 right 不懂,left++,因为 right > target
- left = 0; right = 0;
- 进窗口
sum += sum[right] - 判断
区间内的和 > target
出窗口
sum -= nums[left] - 更新结果
要先判断 sum == target 之后再更新
代码:
class Solution {
public int minOperations(int[] nums, int x) {
int sum = 0;
for(int a : nums){
sum += a;
}
int target = sum - x;
//处理细节
if(target < 0) {
return -1;
}
int ret = -1;
for(int left = 0, right = 0, tmp = 0; right < nums.length; right++) {
tmp += nums[right];//进窗口
while(tmp > target) {
tmp -= nums[left++];
}
if(tmp == target) {
ret = Math.max(ret, right - left + 1);//更新结果
}
}
if(ret == -1) {
return ret;
}else {
return nums.length - ret;
}
}
}
91. 解码方法
原题链接
题干:
对 A-Z可以进行编码
目前我们需要做的,是对已经编码的字母进行解码
但是一个编码有多种解码方式
这样我们可以看出来,在进行编码时,一定需要看到前面的数能不能和后面的数进行结合
比如:06 无法解码,因为 0 不能映射成其他的数
算法原理:
1、状态表示
经验 + 题目要求
以 i 位置为结尾…
dp[i] 表示:以 i 位置为结尾时,解码方式的总数
2、状态转移方程
根据最近的一步,划分问题
在单独解码中有两种情况,一个是成功,一个是失败
在成功中,这个数字肯定是要大于等于1 小于等于9 的
这个时候我们就要看dp[i-1] 时解码方式的总数
还有一种方式叫做联合解码,也是只有两种方式,一个成功,一个失败
在成功中,这个数字肯定大于等于 10 小于等于 26 的
这个时候我们就要看 dp[i-2] 时解码方式的总数
这个时候,我们的状态转移方程就可以求出
dp[i] = dp[i-1] + dp[i-2] (只用在成功的时候才能加上)
3、初始化
dp[0] 可能是0 也可能是1
dp[1] 可能是0 也可能是1 也可能是2
4、填表顺序
从左向右
5、返回值
dp[n-1]
6、代码:
class Solution {
public int numDecodings(String ss) {
int n = ss.length();
char[] s = ss.toCharArray();
int[] dp = new int[n];
if(s[0] != '0') {//初始化第一个位置
dp[0] = 1;
}
if(n == 1) {//处理边界情况
return dp[0];
}
if(s[1] != '0' && s[0] != '0') {//初始化第二个位置
dp[1] += 1;
}
int t = (s[0] - '0') * 10 + s[1] - '0';
if(t >= 10 && t <=26) {
dp[1] += 1;
}
for(int i = 2; i < n; i++) {
//先处理第一种情况
if(s[i] != '0') {
dp[i] += dp[i - 1];
}
//处理第二种情况
int tt = (s[i - 1] - '0') * 10 + s[i] - '0';
if(tt >= 10 && tt <=26) {
dp[i] += dp[i -2];
}
}
return dp[n - 1];
}
}
7、优化
在我们看到上面的代码,发现在初始化dp[1] 和 在循环中的代码非常类似
那么这个时候我们能不能把这两个弄到一起?
能不能再循环中判断 dp[1] 的值呢?
显然是可以的,这个时候,我们就需要创建一个新的dp数组,借用到了一个虚拟节点
注意:
1、虚拟节点里面的值,要保证后面的填表是正确的
2、注意下标的映射关系
dp[2] = dp[1] + dp[0]
这个时候dp[1] 和以前的 dp[0] 是一样的
dp[0] 由于0是虚拟节点,这里需要分析一下
如果开始相加,就说明可以解码成功,这个时候dp[0] 就肯定不是 0
class Solution {
public int numDecodings(String ss) {
int n = ss.length();
char[] s = ss.toCharArray();
int[] dp = new int[n + 1];
dp[0] = 1;//保证后续填表是正确的
if(s[1 - 1] != '0') {//初始化第一个位置
dp[1] = 1;
}
for(int i = 2; i <= n; i++) {
//先处理第一种情况
if(s[i - 1] != '0') {
dp[i] += dp[i - 1];
}
//处理第二种情况
int tt = (s[i - 2] - '0') * 10 + s[i - 1] - '0';
if(tt >= 10 && tt <=26) {
dp[i] += dp[i -2];
}
}
return dp[n];
}
}