第 254 场力扣周赛(KMP贪心快速幂二分+多源bfs并查集 + 时光倒流)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第 254 场力扣周赛(KMP贪心快速幂二分+多源bfs并查集 + 时光倒流)相关的知识,希望对你有一定的参考价值。

第 254 场力扣周赛

稀里糊涂双眼双眼惺忪的做了三道,错了4次。。。还是600来名

5843. 作为子字符串出现在单词中的字符串数目

题目描述

给你一个字符串数组 patterns 和一个字符串 word ,统计 patterns 中有多少个字符串是 word 的子字符串。返回字符串数目。

子字符串 是字符串中的一个连续字符序列。

示例 1:

输入:patterns = [“a”,“abc”,“bc”,“d”], word = “abc”
输出:3
解释:

  • “a” 是 “abc” 的子字符串。
  • “abc” 是 “abc” 的子字符串。
  • “bc” 是 “abc” 的子字符串。
  • “d” 不是 “abc” 的子字符串。
    patterns 中有 3 个字符串作为子字符串出现在 word 中。

示例 2:

输入:patterns = [“a”,“b”,“c”], word = “aaaaabbbbb”
输出:2
解释:

  • “a” 是 “aaaaabbbbb” 的子字符串。
  • “b” 是 “aaaaabbbbb” 的子字符串。
  • “c” 不是 “aaaaabbbbb” 的字符串。
    patterns 中有 2 个字符串作为子字符串出现在 word 中。

示例 3:

输入:patterns = [“a”,“a”,“a”], word = “ab”
输出:3
解释:patterns 中的每个字符串都作为子字符串出现在 word “ab” 中。

提示:

1 <= patterns.length <= 100
1 <= patterns[i].length <= 100
1 <= word.length <= 100
patterns[i] 和 word 由小写英文字母组成

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

思路

子串问题,忘了java中有哪个API可以用了,然后第一道写了十分钟,麻了

class Solution {
    public int numOfStrings(String[] patterns, String word) {
        int res = 0;
        int l = patterns.length;
        int lw = word.length();
        for(int i = 0; i < l; i++){
            String s = patterns[i];
            int k = 0;
            boolean flag = false;
            for(int j = 0; j < lw; j++){
                char c = word.charAt(j);
                int temp = j;
                while(k < s.length() && j < lw && word.charAt(j) == s.charAt(k)){
                    j++;
                    k++;
                    if(k == s.length()){
                        flag = true;
                        break;
                    }
                }
                if(flag)
                    break;
                k = 0;
                j = temp;
            }
            if(flag)
                res++;
        }
        return res;
    }
}

调contians方法,五行…

class Solution {
    public int numOfStrings(String[] patterns, String word) {
        int res = 0;
        for(String s : patterns){
            if(word.contains(s))
                res++;
        }
        return res;
    }   
}

或者indexOf方法

class Solution {
    public int numOfStrings(String[] patterns, String word) {
        int res = 0;
        for(String s : patterns){
            if(word.indexOf(s) != -1)
                res++;
        }
        return res;
    }   
}

KMP再练一手:

class Solution {
    public int numOfStrings(String[] patterns, String word) {
        int res = 0;
        for(String s : patterns){
            int[] next = getNext(s);
            if(kmp(word, s, next))
                res++;
        }
        return res;
    }

    //首先,关键点,构建next的数组,是表示前后缀部分是否有相同
    public int[] getNext(String s){
        int l = s.length();
        int[] next = new int[l];
        int j = 0;
        for(int i = 1; i < l; i++){
            while(j > 0 && s.charAt(i) != s.charAt(j)){
                j = next[j - 1];
            }

            if(s.charAt(i) == s.charAt(j))
                j++;
            next[i] = j;
        }
        return next;
    }   

    public boolean kmp(String s, String t, int[] next){
        int ls = s.length();
        int lt = t.length();
        int j = 0;
        for(int i = 0; i < ls; i++){
            while(j > 0 && s.charAt(i) != t.charAt(j)){
                j = next[j - 1];
            }
            if(s.charAt(i) == t.charAt(j))
                j++;
            if(j == lt)
                return true;
        }
        return false;
    }
}

5832. 构造元素不等于两相邻元素平均值的数组

题目描述

给你一个 下标从 0 开始 的数组 nums ,数组由若干 互不相同的 整数组成。你打算重新排列数组中的元素以满足:重排后,数组中的每个元素都 不等于 其两侧相邻元素的 平均值 。

更公式化的说法是,重新排列的数组应当满足这一属性:对于范围 1 <= i < nums.length - 1 中的每个 i ,(nums[i-1] + nums[i+1]) / 2 不等于 nums[i] 均成立 。

返回满足题意的任一重排结果。

示例 1:

输入:nums = [1,2,3,4,5]
输出:[1,2,4,5,3]
解释:
i=1, nums[i] = 2, 两相邻元素平均值为 (1+4) / 2 = 2.5
i=2, nums[i] = 4, 两相邻元素平均值为 (2+5) / 2 = 3.5
i=3, nums[i] = 5, 两相邻元素平均值为 (4+3) / 2 = 3.5

示例 2:

输入:nums = [6,2,0,9,7]
输出:[9,7,6,2,0]
解释:
i=1, nums[i] = 7, 两相邻元素平均值为 (9+6) / 2 = 7.5
i=2, nums[i] = 6, 两相邻元素平均值为 (7+2) / 2 = 4.5
i=3, nums[i] = 2, 两相邻元素平均值为 (6+0) / 2 = 3

提示:

3 <= nums.length <= 10^5
0 <= nums[i] <= 10^5

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

思路

当时一层循环过不了,两层循环过了,都不知道为啥就过了,实际不太懂怎么做

class Solution {
    public int[] rearrangeArray(int[] nums) {
        //就是不是一个等差数列呗
        int l = nums.length;
        Arrays.sort(nums);
        for(int i = 1; i < l - 1; i++){
            if(nums[i + 1] - nums[i] == nums[i] - nums[i - 1])
                swap(nums, i - 1, i);

        }
        for(int i = 1; i < l - 1; i++){
            if(nums[i + 1] - nums[i] == nums[i] - nums[i - 1])
                swap(nums, i - 1, i);

        }
        return nums;
    }
    
    public void swap(int[] nums, int x, int y){
        int temp = nums[x];
        nums[x] = nums[y];
        nums[y] = temp;
    }
}

然后出来看了一下大佬们的思想:
排序,然后将数组分成小数、大数两部分,然后小、大、小、大穿插着放
其实报错的示例就是这么安排的,但是我当时没仔细看错了给定正确答案,我还以为是随便排的
又学到了一个小技巧

class Solution {
    public int[] rearrangeArray(int[] nums) {
        Arrays.sort(nums);
        int l = nums.length;
        int[] res = new int[l];
        int idx = 0;
        for(int i = 0; i < l; i += 2){
            res[i] = nums[idx++]; 
        }
        for(int i = 1; i < l; i += 2){
            res[i] = nums[idx++];
        }
        return res;
    }
}

或者直接交换相邻位置

class Solution {
    public int[] rearrangeArray(int[] nums) {
        //直接交换相邻位,道理还是那个
        //交换以后,会形成大小大小大...这种形式
        Arrays.sort(nums);
        int l = nums.length;
        for(int i = 0; i < l - 1; i += 2){
            int temp = nums[i];
            nums[i] = nums[i + 1];
            nums[i + 1] = temp;
        }
        return nums;
    }
}

5844. 数组元素的最小非零乘积

题目描述

给你一个正整数 p 。你有一个下标从 1 开始的数组 nums ,这个数组包含范围 [1, 2p - 1] 内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:

从 nums 中选择两个元素 x 和 y 。
选择 x 中的一位与 y 对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。
比方说,如果 x = 1101 且 y = 0011 ,交换右边数起第 2 位后,我们得到 x = 1111 和 y = 0001 。

请你算出进行以上操作 任意次 以后,nums 能得到的 最小非零 乘积。将乘积对 109 + 7 取余 后返回。

注意:答案应为取余 之前 的最小值。

示例 1:

输入:p = 1
输出:1
解释:nums = [1] 。
只有一个元素,所以乘积为该元素。

示例 2:

输入:p = 2
输出:6
解释:nums = [01, 10, 11] 。
所有交换要么使乘积变为 0 ,要么乘积与初始乘积相同。
所以,数组乘积 1 * 2 * 3 = 6 已经是最小值。

示例 3:

输入:p = 3
输出:1512
解释:nums = [001, 010, 011, 100, 101, 110, 111]

  • 第一次操作中,我们交换第二个和第五个元素最左边的数位。
    - 结果数组为 [001, 110, 011, 100, 001, 110, 111] 。
  • 第二次操作中,我们交换第三个和第四个元素中间的数位。
    - 结果数组为 [001, 110, 001, 110, 001, 110, 111] 。
    数组乘积 1 * 6 * 1 * 6 * 1 * 6 * 7 = 1512 是最小乘积。

提示:

1 <= p <= 60

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

思路

看到最小乘积,当时想的是正方形面积最大,而两条边差距越大的话,面积越小
所以要想整体乘积越小,就得数之间的差距越大
而看到示例中的第三个也印证了我的想法
然后再找规律,给定p,那么共有2^p-1个数,最大数也是2^p-1
而剩下的数,都可以通过交换,变成1和2^p-2(用base代替)
所以总共就有(2^p-2) / 2(用x代替)个base
也就是base的x次方
因为数很大,刚开始我想用double算的,结果60弄进去,还是溢出了
最后只能快速幂,然后每次取余了

对了,乘法取余:(a×b) mod c=(a mod c * b mod c) mod c 记住

class Solution {
    public static final int MOD = (int)1.0e9 + 7;
    public int minNonZeroProduct(int p) {
        //使数尽可能在两边
        //最小是
        //前后4位不能动
        if(p == 1)
            return 1;
        if(p == 2)
            return 6;
        long t = (long)Math.pow(2, p);
        long x = (t - 3) / 2;
        long base = t - 2;
        long ret = mypow(base, x + 1);
        //System.out.println(t);
        ret = ((ret % MOD) * ((t - 1) % MOD)) % MOD;
        return (int)ret;
    }
    
    public long mypow(long base, long x){
        long res = 1;
        base = base % MOD;
        int t = 1;
        while(x != 0){
            if((x & 1) == 1){
                res = (res * (base % MOD)) % MOD;
            }
            x >>= 1;
            base = ((base % MOD) * (base % MOD)) % MOD;
        }
        return res;
    }
}

5845. 你能穿过矩阵的最后一天

题目描述

给你一个下标从 1 开始的二进制矩阵,其中 0 表示陆地,1 表示水域。同时给你 row 和 col 分别表示矩阵中行和列的数目。

一开始在第 0 天,整个 矩阵都是 陆地 。但每一天都会有一块新陆地被 水 淹没变成水域。给你一个下标从 1 开始的二维数组 cells ,其中 cells[i] = [ri, ci] 表示在第 i 天,第 ri 行 ci 列(下标都是从 1 开始)的陆地会变成 水域 (也就是 0 变成 1 )。

你想知道从矩阵最 上面 一行走到最 下面 一行,且只经过陆地格子的 最后一天 是哪一天。你可以从最上面一行的 任意 格子出发,到达最下面一行的 任意 格子。你只能沿着 四个 基本方向移动(也就是上下左右)。

请返回只经过陆地格子能从最 上面 一行走到最 下面 一行的 最后一天 。

示例 1:

输入:row = 2, col = 2, cells = [[1,1],[2,1],[1,2],[2,2]]
输出:2
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 2 天。

示例 2:

输入:row = 2, col = 2, cells = [[1,1],[1,2],[2,1],[2,2]]
输出:1
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 1 天。

示例 3:

输入:row = 3, col = 3, cells = [[1,2],[2,1],[3,3],[2,2],[1,1],[1,3],[2,3],[3,2],[3,1]]
输出:3
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 3 天。

提示:

2 <= row, col <= 2 * 10^4
4 <= row * col <= 2 * 10^4
cells.length == row * col
1 <= ri <= row
1 <= ci <= col
cells 中的所有格子坐标都是 唯一 的。

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

思路

这道题我看范围,实在没想到用dfs能过,当时觉得dfs要走的情况很多,一直在想怎么把水域连成一条线啥的。。。
现在想想,也就是把所有方格里的点都遍历一次,复杂度也就是10的4次,在加个二分,肯定超时不了。。。失策了
这个题挺简单的

思路就是二分找能通过的天数,然后查看一天是否能通过,用bfs或者dfs

第一种:二分加多源bfs

class Solution {
    int[][] dir = {{0,1},{0,-1},{1,0},{-1,0}};
    public int latestDayToCross(int row, int col, int[][] cells) {
        int left = 0;
        int right = row * col;
        while(left < right){
            int mid = (right - left + 1) / 2 + left;
            //构建图
            int[][] graph = new int[row][col];
            //将水域赋值为1,是将mid这一天之前的水域
            for(int i = 0; i < mid; i++){
                //水域下标从1开始的
                int r = cells[i][0] - 1;
                int c = cells[i][1] - 1;
                graph[r][c] = 1;
            }

            boolean[][] used = new boolean[row][col];
            Queue<int[]> queue = new LinkedList<>();
            //将第一行的所有陆地加入队列中
            for(int i = 0; i < col; i++){
                if(graph[0][i] == 0)
                    queue.offer(new int[]{0, i});
                    used[0][i] = true;
            }

            //bfs找通路
            boolean found = false;
            while(!queue.isEmpty()){
                int[] point = queue.poll();
                for(int[] d : dir){
                    int nx = point[0] + d[0];
                    int ny = point[1] + d[1];
                    if(nx >= 0 && nx < row && ny >= 0 && ny < col){
                        //如果遍历过了,返回
                        if(used[nx][ny])
                            continue;
                        //如果是水,跳过
                        if(graph[nx][ny] == 1)
                            continue;
                        //如果到达了最后一行,返回
                        if(nx == row - 1){
                            found = true;
                            break;
                        }
                        queue.offer(new int[]{nx, ny});
                        used[nx][ny] LeetCode第 57 场力扣夜喵双周赛(差分数组单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)

第 256 场力扣周赛(状态压缩+dp,二进制子序列的动规940)

LeetCode494. 目标和 / 474. 一和零 / 203. 移除链表元素 / 第 244 场力扣周赛

力扣周赛有含金量吗

LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))

解题报告力扣 第 287 场周赛