目录
[一,3402. 使每一列严格递增的最少操作次数](#一,3402. 使每一列严格递增的最少操作次数)
[二,3403. 从盒子中找出字典序最大的字符串 I](#二,3403. 从盒子中找出字典序最大的字符串 I)
[三,3404. 统计特殊子序列的数目](#三,3404. 统计特殊子序列的数目)
[四,3405. 统计恰好有 K 个相等相邻元素的数组数目](#四,3405. 统计恰好有 K 个相等相邻元素的数组数目)
一,3402. 使每一列严格递增的最少操作次数

将每一列变成严格递增的,且只能使用加法操作,需要的操作次数。直接依次枚举每一列:
- 对于 nums[0],它肯定不变(只能执行加法操作)
- 对于 nums[i:],如果 nums[i] > nums[i-1],不需要操作;如果 nums[i] <= nums[i-1],必须要将 nums[i] 变成 nums[i-1] + 1,也就是增加 nums[i-1] + 1 - nums[i]
代码如下:
class Solution {
public int minimumOperations(int[][] grid) {
int ans = 0;
for(int j=0; j<grid[0].length; j++){
int pre = grid[0][j];
for(int i=1; i<grid.length; i++){
ans += Math.max(pre - grid[i][j] + 1, 0);
pre = Math.max(pre+1, grid[i][j]);
}
}
return ans;
}
}
二,3403. 从盒子中找出字典序最大的字符串 I

求字典序最大的字符串,如果枚举左右端点,需要 O(n^3) 的时间复杂度(比较字符串大小需要O(n)),会超时,有没有更快的做法?
可以发现在字符串前缀相同的情况下,字符串的长度一定是越长越好,也就是说,可以直接枚举左端点,右端点 = 左端点 + 字符串的最大长度 ,注意这里的右端点要和 word.length 取一个较小值。
那么本题字符串最长可以取到哪里,题目说将 word 分成 numFriends 段,取最长,那么将 numsFriends - 1 段的长度取 1,可以取到的最长长度就是 word.length - (numsFriends - 1) 。
代码如下:
class Solution {
public String answerString(String s, int op) {
if(op == 1) return s;
int n = s.length();
int mx = n - (op - 1);
String ans = "";
for(int i=0; i<n; i++){
String sub = s.substring(i, Math.min(i+mx,n));
if(sub.compareTo(ans) > 0){
ans = sub;
}
}
return ans;
}
}
三,3404. 统计特殊子序列的数目

本题求满足 nums[p] * nums[r] = nums[q] * nums[s] 且 p < r < q < s 的子序列的个数,假设按照从前往后的顺序将四个数分为 a,b,c,d,也就是求 a * c = b * d 的子序列个数,将该式进行转换,得到 a / b = d / c,这样就按下标顺序把 a,b 放到一起,c,d 放到一起,可以用前后缀来做了。

代码如下:
class Solution {
public long numberOfSubsequences(int[] nums) {
int n = nums.length;
Map<Float, Integer> map = new HashMap<>();
long ans = 0;
for(int i=2; i<n-4; i++){
float b = nums[i];
for(int j=0; j<i-1; j++){
float a = nums[j];
map.merge(a/b, 1, Integer::sum);
}
float c = nums[i+2];
for(int j=i+4; j<n; j++){
float d = nums[j];
ans += map.getOrDefault(d/c, 0);
}
}
return ans;
}
}
上述做法是建立在数据范围小,使用 float 精确度足够的情况下,还有一种更加通用的做法,,将 :b: 通分一下,就是
,这样可以存储 1/3 这种数字,代码如下:
class Solution {
public long numberOfSubsequences(int[] nums) {
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();
long ans = 0;
for(int i=2; i<n-4; i++){
int b = nums[i];
for(int j=0; j<i-1; j++){
int a = nums[j];
int g = gcd(a, b);
map.merge((a/g)<<16|(b/g), 1, Integer::sum);
}
int c = nums[i+2];
for(int j=i+4; j<n; j++){
int d = nums[j];
int g = gcd(c, d);
ans += map.getOrDefault((d/g)<<16|(c/g), 0);
}
}
return ans;
}
int gcd(int x, int y){
return y==0?x:gcd(y,x%y);
}
}
四,3405. 统计恰好有 K 个相等相邻元素的数组数目

本题就是单纯的排列组合,题目要求恰好有 k 个下标满足 arr[i-1] = arr[i],对于一个数组 nums 来说,他一共有 n - 1 个相邻位置,其中 k 个位置要使得 arr[i-1] = arr[i],反过来就是有 n - 1 - k 个位置 arr[i-1] != arr[i],即在 n - 1 个位置选择 n - 1 - k 个位置来插排,且相邻的两个部分元素不同,对于第一个部分可以选择 m 个数,第二个部分可以选择 m-1 个数,第三、四、...... n-k 个部分都可以选择 m-1 个数。即
代码如下:
class Solution {
private static final int MOD = 1_000_000_007;
private static final int MX = 100_000;
private static final long[] fac = new long[MX];
private static final long[] invf = new long[MX];
//1/(a * b) % MOD = pow(a*b, MOD-2), MOD必须是质数
//invf[i] = 1 / i! % MOD = pow(i!, MOD-2)
//invf[i-1] = 1/(i-1)! % MOD = pow((i-1)!, MOD-2)
//invf[i-1] = invf[i] * i
static{
fac[0] = 1;
for(int i=1; i<MX; i++){
fac[i] = fac[i-1] * i % MOD;
}
invf[MX - 1] = pow(fac[MX-1], MOD-2);
for(int i=MX-1; i>0; i--){
invf[i-1] = invf[i] * i % MOD;
}
}
static long pow(long a, int b){
long res = 1;
while(b > 0){
if(b % 2 == 1)
res = res * a % MOD;
a = a * a % MOD;
b /= 2;
}
return res;
}
private long comb(int n, int m){
// n!/m!*(n-m)!
return fac[n] * invf[m] % MOD * invf[n-m] % MOD;
}
public int countGoodArrays(int n, int m, int k) {
return (int)(comb(n-1, k) * m % MOD * pow(m-1, n-1-k) % MOD);
}
}