【算法刷题】Day13

文章目录

  • 1658. 将 x 减到 0 的最小操作数
    • 题干:
    • 算法原理:滑动窗口
    • 代码:
  • 91. 解码方法
    • 题干:
    • 算法原理:
      • 1、状态表示
      • 2、状态转移方程
      • 3、初始化
      • 4、填表顺序
      • 5、返回值
      • 6、代码:
      • 7、优化

1658. 将 x 减到 0 的最小操作数

【算法刷题】Day13_第1张图片
原题链接


题干:

一个整数数组 nums
一个整数 x
移除整数数组最左或最右的值,x减去这个值
一直如此,x = 0 就返回最小操作数,要不然就返回 -1
【算法刷题】Day13_第2张图片

算法原理:滑动窗口

在上面的题干解析中,我们通过图可以发现,我们要找的是左边加右边等于 x
这样我们如果设整个数组的和为 sum
减去的为 x,那么中间的定义为 target = sum - x
这个时候,我们可以直接找中间区间等于 target 的最长区间

转化为:找出最长子数组的长度,所有元素的和正好等于 sum-x

【算法刷题】Day13_第3张图片
由于left 到 right 之间的值肯定 >= target
这个时候,我们就可以让 right 不懂,left++,因为 right > target

  1. left = 0; right = 0;
  2. 进窗口
    sum += sum[right]
  3. 判断
    区间内的和 > target
    出窗口
    sum -= nums[left]
  4. 更新结果
    要先判断 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;
        }
    }
}

【算法刷题】Day13_第4张图片

91. 解码方法

【算法刷题】Day13_第5张图片
【算法刷题】Day13_第6张图片

原题链接


题干:

对 A-Z可以进行编码
目前我们需要做的,是对已经编码的字母进行解码
但是一个编码有多种解码方式
【算法刷题】Day13_第7张图片
这样我们可以看出来,在进行编码时,一定需要看到前面的数能不能和后面的数进行结合

比如:06 无法解码,因为 0 不能映射成其他的数

算法原理:

1、状态表示

经验 + 题目要求
以 i 位置为结尾…

dp[i] 表示:以 i 位置为结尾时,解码方式的总数

2、状态转移方程

根据最近的一步,划分问题
【算法刷题】Day13_第8张图片
在单独解码中有两种情况,一个是成功,一个是失败
在成功中,这个数字肯定是要大于等于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];
    }
}

【算法刷题】Day13_第9张图片

7、优化

在我们看到上面的代码,发现在初始化dp[1] 和 在循环中的代码非常类似
那么这个时候我们能不能把这两个弄到一起?
能不能再循环中判断 dp[1] 的值呢?

显然是可以的,这个时候,我们就需要创建一个新的dp数组,借用到了一个虚拟节点
【算法刷题】Day13_第10张图片
注意:
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];
    }
}

【算法刷题】Day13_第11张图片

你可能感兴趣的:(算法刷题,算法,java)