简单
给你一个下标从 0 开始的二维整数数组 nums
表示汽车停放在数轴上的坐标。对于任意下标 i
,nums[i] = [starti, endi]
,其中 starti
是第 i
辆车的起点,endi
是第 i
辆车的终点。
返回数轴上被车 任意部分 覆盖的整数点的数目。
示例 1:
输入:nums = [[3,6],[1,5],[4,7]]
输出:7
解释:从 1 到 7 的所有点都至少与一辆车相交,因此答案为 7 。
示例 2:
输入:nums = [[1,3],[5,8]]
输出:7
解释:1、2、3、5、6、7、8 共计 7 个点满足至少与一辆车相交,因此答案为 7 。
提示:
1 <= nums.length <= 100
nums[i].length == 2
1 <= starti <= endi <= 100
class Solution {
public int numberOfPoints(List<List<Integer>> nums) {
int n = 101;
int[] diff = new int[n+5];
for(List<Integer> list: nums){
int x = list.get(0), y = list.get(1);
diff[x] += 1;
diff[y+1] -= 1;
}
int res = 0;
int sum = 0;
for(int x : diff){
sum += x;
if(sum > 0) res += 1;
}
return res;
}
}
中等
给你四个整数 sx
、sy
、fx
、fy
以及一个 非负整数 t
。
在一个无限的二维网格中,你从单元格 (sx, sy)
开始出发。每一秒,你 必须 移动到任一与之前所处单元格相邻的单元格中。
如果你能在 恰好 t
秒 后到达单元格 (fx, fy)
,返回 true
;否则,返回 false
。
单元格的 相邻单元格 是指该单元格周围与其至少共享一个角的 8 个单元格。你可以多次访问同一个单元格。
示例 1:
输入:sx = 2, sy = 4, fx = 7, fy = 7, t = 6
输出:true
解释:从单元格 (2, 4) 开始出发,穿过上图标注的单元格,可以在恰好 6 秒后到达单元格 (7, 7) 。
示例 2:
输入:sx = 3, sy = 1, fx = 7, fy = 3, t = 3
输出:false
解释:从单元格 (3, 1) 开始出发,穿过上图标注的单元格,至少需要 4 秒后到达单元格 (7, 3) 。 因此,无法在 3 秒后到达单元格 (7, 3) 。
提示:
1 <= sx, sy, fx, fy <= 109
0 <= t <= 109
https://leetcode.cn/problems/determine-if-a-cell-is-reachable-at-a-given-time/solutions/2435696/zui-da-keng-dian-zai-yu-qi-dian-he-zhong-o2fg/
这道题又是非常像CF低段位场的签到题风格。注意到任何一步都可以拆成两步,斜走一步可以拆成横着走一步加上竖着走一步,而横或竖走一步也能拆成先斜走一步再反方向走一步(例如往右一步能等效成先右上走一步再往下走一步)。所以对于一般的情况,只要起点到终点的最少步数不超过t,那么总是有解的,多余的步数总可以浪费掉。
class Solution {
/**
唯一的“不一般情况”就是当起点与终点重合且只能走1步时是无解的。
起点终点重合且t>1时,无论第一步怎么走,剩下t-1步都有办法返回。
*/
public boolean isReachableAtTime(int sx, int sy, int fx, int fy, int t) {
if(sx == fx && sy == fy){
return t == 1 ? false : true;
}
int mx = Math.abs(sx - fx) + Math.abs(sy - fy); // 最多步数
int mn = Math.max(Math.abs(sx - fx), Math.abs(sy - fy)); // 最少步数
return t >= mn;
}
}
中等
给你一个大小为 3 * 3
,下标从 0 开始的二维整数矩阵 grid
,分别表示每一个格子里石头的数目。网格图中总共恰好有 9
个石头,一个格子里可能会有 多个 石头。
每一次操作中,你可以将一个石头从它当前所在格子移动到一个至少有一条公共边的相邻格子。
请你返回每个格子恰好有一个石头的 最少移动次数 。
示例 1:
输入:grid = [[1,1,0],[1,1,1],[1,2,1]]
输出:3
解释:让每个格子都有一个石头的一个操作序列为:
1 - 将一个石头从格子 (2,1) 移动到 (2,2) 。
2 - 将一个石头从格子 (2,2) 移动到 (1,2) 。
3 - 将一个石头从格子 (1,2) 移动到 (0,2) 。
总共需要 3 次操作让每个格子都有一个石头。
让每个格子都有一个石头的最少操作次数为 3 。
示例 2:
输入:grid = [[1,3,0],[1,0,0],[1,0,3]]
输出:4
解释:让每个格子都有一个石头的一个操作序列为:
1 - 将一个石头从格子 (0,1) 移动到 (0,2) 。
2 - 将一个石头从格子 (0,1) 移动到 (1,1) 。
3 - 将一个石头从格子 (2,2) 移动到 (1,2) 。
4 - 将一个石头从格子 (2,2) 移动到 (2,1) 。
总共需要 4 次操作让每个格子都有一个石头。
让每个格子都有一个石头的最少操作次数为 4 。
提示:
grid.length == grid[i].length == 3
0 <= grid[i][j] <= 9
grid
中元素之和为 9
。https://leetcode.cn/problems/minimum-moves-to-spread-stones-over-grid/solutions/2435313/tong-yong-zuo-fa-zui-xiao-fei-yong-zui-d-iuw8/
class Solution {
public int minimumMoves(int[][] grid) {
List<int[]> from = new ArrayList<>();
List<int[]> to = new ArrayList<>();
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[i].length; j++){
if(grid[i][j] > 1){
for(int k = 1; k < grid[i][j]; k++)
from.add(new int[]{i, j});
}else if(grid[i][j] == 0){
to.add(new int[]{i, j});
}
}
}
int ans = Integer.MAX_VALUE;
// 枚举from的全排列
for(List<int[]> from2 : permutations(from)){
int total = 0;
for(int i = 0; i < from2.size(); i++){
int[] f = from2.get(i);
int[] t = to.get(i);
total += Math.abs(f[0] - t[0]) + Math.abs(f[1] - t[1]);
}
ans = Math.min(ans, total);
}
return ans;
}
private List<List<int[]>> permutations(List<int[]> arr) {
List<List<int[]>> result = new ArrayList<>();
permute(arr, 0, result);
return result;
}
private void permute(List<int[]> arr, int start, List<List<int[]>> result) {
if (start == arr.size()) {
result.add(new ArrayList<>(arr));
}
for (int i = start; i < arr.size(); i++) {
swap(arr, start, i);
permute(arr, start + 1, result);
swap(arr, start, i);
}
}
private void swap(List<int[]> arr, int i, int j) {
int[] temp = arr.get(i);
arr.set(i, arr.get(j));
arr.set(j, temp);
}
}
困难
给你两个长度都为 n
的字符串 s
和 t
。你可以对字符串 s
执行以下操作:
s
长度为 l
(0 < l < n
)的 后缀字符串 删除,并将它添加在 s
的开头。s = 'abcd'
,那么一次操作中,你可以删除后缀 'cd'
,并将它添加到 s
的开头,得到 s = 'cdab'
。给你一个整数 k
,请你返回 恰好 k
次操作将 s
变为 t
的方案数。
由于答案可能很大,返回答案对 109 + 7
取余 后的结果。
示例 1:
输入:s = "abcd", t = "cdab", k = 2
输出:2
解释:
第一种方案:
第一次操作,选择 index = 3 开始的后缀,得到 s = "dabc" 。
第二次操作,选择 index = 3 开始的后缀,得到 s = "cdab" 。
第二种方案:
第一次操作,选择 index = 1 开始的后缀,得到 s = "bcda" 。
第二次操作,选择 index = 1 开始的后缀,得到 s = "cdab" 。
示例 2:
输入:s = "ababab", t = "ababab", k = 1
输出:2
解释:
第一种方案:
选择 index = 2 开始的后缀,得到 s = "ababab" 。
第二种方案:
选择 index = 4 开始的后缀,得到 s = "ababab" 。
提示:
2 <= s.length <= 5 * 105
1 <= k <= 1015
s.length == t.length
s
和 t
都只包含小写英文字母。https://leetcode.cn/problems/string-transformation/solutions/2435348/kmp-ju-zhen-kuai-su-mi-you-hua-dp-by-end-vypf/
class Solution {
public int numberOfWays(String s, String t, long k) {
int n = s.length();
int c = kmpSearch(s + s.substring(0, n - 1), t);
long[][] m = {
{c - 1, c},
{n - c, n - 1 - c},
};
m = pow(m, k);
return s.equals(t) ? (int) m[0][0] : (int) m[0][1];
}
// KMP 模板
private int[] calcMaxMatch(String s) {
int[] match = new int[s.length()];
int c = 0;
for (int i = 1; i < s.length(); i++) {
char v = s.charAt(i);
while (c > 0 && s.charAt(c) != v) {
c = match[c - 1];
}
if (s.charAt(c) == v) {
c++;
}
match[i] = c;
}
return match;
}
// KMP 模板
// 返回 text 中出现了多少次 pattern(允许 pattern 重叠)
private int kmpSearch(String text, String pattern) {
int[] match = calcMaxMatch(pattern);
int lenP = pattern.length();
int matchCnt = 0;
int c = 0;
for (int i = 0; i < text.length(); i++) {
char v = text.charAt(i);
while (c > 0 && pattern.charAt(c) != v) {
c = match[c - 1];
}
if (pattern.charAt(c) == v) {
c++;
}
if (c == lenP) {
matchCnt++;
c = match[c - 1];
}
}
return matchCnt;
}
private static final long MOD = (long) 1e9 + 7;
// 矩阵乘法
private long[][] multiply(long[][] a, long[][] b) {
long[][] c = new long[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = (a[i][0] * b[0][j] + a[i][1] * b[1][j]) % MOD;
}
}
return c;
}
// 矩阵快速幂
private long[][] pow(long[][] a, long n) {
long[][] res = {{1, 0}, {0, 1}};
for (; n > 0; n /= 2) {
if (n % 2 > 0) {
res = multiply(res, a);
}
a = multiply(a, a);
}
return res;
}
}