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

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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);
        
    }
}

以上是关于LeetCode523. 连续的子数组和/394. 字符串解码/牛客:万万没想到之抓捕孔连顺的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode523. 连续的子数组和

LeetCode523. 连续的子数组和

leetcode No.523 连续的子数组和 java

leetcode中等523连续的子数组和

LeetCode每日一题——523. 连续的子数组和(同余定理)

LeetCode 523 连续的子数组和[前缀和 哈希表] HERODING的LeetCode之路