第 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场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))