LeetCode523. 连续的子数组和/394. 字符串解码/牛客:万万没想到之抓捕孔连顺

523. 连续的子数组和

2021.6.2每日一题

题目描述
给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。


示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
示例 2:

输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。 
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
示例 3:

输入:nums = [23,2,6,4,7], k = 13
输出:false

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/continuous-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

和前几天做的560.和为K的子数组一样,只不过这里和变成k的倍数,那么这个题如果沿用那种题的思路,就是前缀和加哈希表处理,并且哈希表键的部分存储的是前缀和,会发现还是需要遍历哈希表键的部分或者遍历k的倍数,来找符合条件的值
这里发现,pre[i] - pre[t] 如果等于k的倍数,那么两个值对k的余数肯定相同,所以想到哈希表中存储前缀和的余数
但是还有个条件就是至少子数组中有两个数,也就是i - t >= 2,那么就会出现另一种特殊情况,就是相邻的两个前缀和都是 k 的倍数,而此时,我们要做的处理就是只存储前面一个值,而后面的值忽略。因为如果能和后者一起满足条件的值肯定和前者也可以满足条件

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        //一般而言,两层循环,也就求出来了,预处理前缀和或者动态规划都可以
        //但是吧,前几天做了和为k的子数组,感觉还是可以看看一层循环行不行
        int n = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        
        int pre = 0;
        map.put(0, -1);
        for(int i = 0; i < n; i++){
            pre += nums[i];
            //因为如果有pre[i] - pre[t] == 0;那么两个数的余数肯定相同
            //所以说,哈希表中存余数?
            //取余
            int t = pre % k;
            if(map.containsKey(t)){
                if(i - map.get(t) >= 2)
                    return true;
            }else
                map.put(t, i);
        }
        return false;
    }
}

394. 字符串解码

题目描述
给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。


示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"
示例 2:

输入:s = "3[a2[c]]"
输出:"accaccacc"
示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

前几天有个字符串题的同类型题
我想到的方法,从左到右遍历,如果碰到数字,存储到栈中
如果看到左括号,也将下标存储到栈中,如果看到右括号,取出左括号的位置,并取出与它相邻的一个数字
然后截取字符串,并且将该字符串重复k次,和原字符串拼接。继续遍历,直到处理完整个字符串
题解思路,是把字母也放在栈中处理,其实都一样,应该会更简单一点,因为下标不需要处理
这里直接贴一个K神的,学习一下简介的代码思路:

class Solution {
    public String decodeString(String s) {
        StringBuilder res = new StringBuilder();
        int multi = 0;
        LinkedList<Integer> stack_multi = new LinkedList<>();
        LinkedList<String> stack_res = new LinkedList<>();
        for(Character c : s.toCharArray()) {
            if(c == '[') {
            	//遇到左括号,将数字和临时结果入栈,并重新赋给初始值
                stack_multi.addLast(multi);
                stack_res.addLast(res.toString());
                multi = 0;
                res = new StringBuilder();
            }
            //如果遇到右括号,拼接字符串,当前栈中最后的字符串和重复k次的当前字符串拼接
            else if(c == ']') {
                StringBuilder tmp = new StringBuilder();
                int cur_multi = stack_multi.removeLast();
                for(int i = 0; i < cur_multi; i++) 
                	tmp.append(res);
                res = new StringBuilder(stack_res.removeLast() + tmp);
            }
            //如果遇到数字,记录数字
            else if(c >= '0' && c <= '9') multi = multi * 10 + Integer.parseInt(c + "");
            //遇到字符,添加到res中
            else res.append(c);
        }
        return res.toString();
    }
}

作者:jyd
链接:https://leetcode-cn.com/problems/decode-string/solution/decode-string-fu-zhu-zhan-fa-di-gui-fa-by-jyd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

万万没想到之抓捕孔连顺

题目描述

链接:https://www.nowcoder.com/questionTerminal/c0803540c94848baac03096745b55b9b
来源:牛客网

我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议

1. 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。

我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!
……
万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!

请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
1. 两个特工不能埋伏在同一地点
2. 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用


输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)

第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)


输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模
示例1
输入
4 3
1 2 3 4
输出
4
说明
可选方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)

示例2
输入
5 19
1 10 20 30 50
输出
1
说明
可选方案 (1, 10, 20)
思路

这个题挺有意思的其实,滑动窗口。但是很快会想到,窗口内的值会重复,然后就会想到每次计算固定选择了right一个位置,在剩下符合条件的位置中选两个位置,就是一个组合数的求解C(n,2)
但是刚开始的窗口呢,我开始比较纠结要不要单独处理,但是后来想通了,开始固定长度为3,和上面的逻辑一起进行就可以了
另外,需要注意的是,取余的要求,只是在计算res的时候取余还是不行,需要将结果变量的类型定义为long,因为两个数相乘可能会越界。
字节的题确实不好做啊!

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        //感觉是滑动窗口,就是符合条件的窗口内取值,就相当于固定一个人在末尾
        //然后取当前的方案数
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int D = sc.nextInt();
        int[] pos = new int[N];
        for(int i = 0; i < N; i++){
            pos[i] = sc.nextInt();
        }
        int left = 0;
        int right = 2;
        long res = 0;
        while(right < N){
            //先找到一个区间
            while(pos[right] - pos[left] > D){
                left++;
            }
       
            //然后计算当前区间内的方案数,此时是以right为结尾,
            //相当于在n-1里面选2个数,组合数的求解
            long n = right - left + 1;
            if(n >= 3){
                res = (res + (n - 1) * (n - 2) / 2) % 99997867;
            }
            right++;
        }
        System.out.println(res);
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠问题和最优结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值