本文正在参加「金石计划 . 瓜分6万现金大奖」
775. 全局倒置与局部倒置(难度:中等)
一、题目
给你一个长度为 n 的整数数组 nums ,表示由范围 [0, n - 1] 内所有整数组成的一个排列。 全局倒置 的数目等于满足下述条件不同下标对 (i, j) 的数目:
- 0 <= i < j < n
- nums[i] > nums[j]
局部倒置 的数目等于满足下述条件的下标 i 的数目:
- 0 <= i < n - 1
- nums[i] > nums[i + 1]
当数组 nums 中 全局倒置 的数量等于 局部倒置 的数量时,返回 true ;否则,返回 false 。
二、示例
2.1> 示例 1:
【输入】nums = [1,0,2]
【输出】true
【解释】有 1 个全局倒置,和 1 个局部倒置。
2.2> 示例 2:
【输入】nums = [1,2,0]
【输出】false
【解释】有 2 个全局倒置,和 1 个局部倒置。
提示:
- n == nums.length
1<= n <=50000<= nums[i] <nnums中的所有整数 互不相同nums是范围[0, n - 1]内所有数字组成的一个排列
三、解题思路
3.1> 根据前缀的最大值来判断
根据题目描述,我们可以得到如下结论:
如果是局部倒置,那么一定就是全局倒置。所以,全局倒置是包含局部倒置的。
那么我们就可以将解题视角放在非局部倒置的全局倒置上。换句话说,也就是—— 非相邻数字是否满足递增。 具体操作如下图所示:
3.2> 根据偏移的差值来判断
由于题目中已经给出了如下一个关键条件:
数组
nums长度为n,并且数字是由0到n-1构成的。
所以,就可以通过nums[i]-i计算出i位置的元素与有序后的位置之间的差值:
【差值等于0】表示元素i所在的位置就是排序后的位置。
【差值等于1】表示元素1所在的位置向前1位或向后1位。
【其他情况】表示元素所在位置偏差大于1位,也就是出现了全局倒置并且非局部倒置的情况。
具体操作如下图所示:
四、代码实现
4.1> 根据前缀的最大值来判断
class Solution {
public boolean isIdealPermutation(int[] nums) {
int max = nums[0];
for (int i = 2; i < nums.length; i++) {
if (nums[i] < max) return false;
max = Math.max(max, nums[i - 1]);
}
return true;
}
}
4.2> 根据偏移的差值来判断
class Solution {
public boolean isIdealPermutation(int[] nums) {
for (int i = 0; i < nums.length; i++)
if (Math.abs(nums[i] - i) > 1) return false;
return true;
}
}
792. 匹配子序列的单词数(难度:中等)
一、题目
给定字符串 s 和字符串数组 words, 返回 words[i] 中是s的子序列的单词个数 。
字符串的 子序列 是从原始字符串中生成的新字符串,可以从中删去一些字符(可以是none),而不改变其余字符的相对顺序。
例如, “ace” 是 “abcde” 的子序列。
二、示例
2.1> 示例 1:
【输入】 s = "abcde", words = ["a","bb","acd","ace"]
【输出】 3
【解释】 有三个是 s 的子序列的单词: "a", "acd", "ace"。
2.2> 示例 2:
【输入】 s = "dsahjpjauf", words = ["ahjpjau","ja","ahbwzgqnuk","tnmlanowax"]
【输出】 2
提示:
1<= s.length <=5 * 10^41<= words.length <=50001<= words[i].length <=50words[i]和s都只由小写字母组成。
三、解题思路
根据题目描述,需要我们去words字符串数组中却判断,哪些是字符串s的子序列,最后再将子序列的总个数返回回来。那么,对于字符串子序列,我们主要关心如下两点:
【是否存在?】子序列中的某个字符是否在字符串s中存在。
【顺序对吗?】子序列中字符出现的顺序是否违背了字符串s中的顺序。
那么针对这两种关注点,我们首先遍历字符串s中的每个字符,由于这些字符都是由小写字母构成,所以我们可以通过采用:字符减去‘a’来确定下标位置,并将该字符在s中出现的位置保存到ArrayList集合中。
然后,我们再分别遍历字符串数组words中的每个字符串,逐一判断每个字符出现的位置顺序是否与s相同,如果不同,则可以判断该字符串不是s的子序列。具体操作详情请见下图:
四、代码实现
class Solution {
public int numMatchingSubseq(String s, String[] words) {
List<Integer>[] sm = new ArrayList[26]; // index:字符 sm[index]:字符出现的位置集合
char[] sc = s.toCharArray();
for (int i = 0; i < sc.length; i++) {
if (sm[sc[i]-'a'] == null) sm[sc[i]-'a'] = new ArrayList<>();
sm[sc[i]-'a'].add(i);
}
int result = words.length; // 初始化result数量为所有单词,如果不满足条件,则陆续执行减1操作
for (String word : words) { // 遍历每个单词
int compareIndex = -1, index;
for (int i = 0; i < word.length(); i++) { // 遍历每个字符
if (sm[word.charAt(i)-'a'] == null ||
((index = findCharIndex(compareIndex, sm[word.charAt(i)-'a'])) <= compareIndex)) {
result--;
break;
}
compareIndex = index;
}
}
return result;
}
// 折半查找
private int findCharIndex(int compareIndex, List<Integer> list) {
int head = 0, tail = list.size() - 1, mid;
while (head < tail) {
mid = head + (tail - head) / 2;
if (list.get(mid) > compareIndex) tail = mid;
else head = ++mid;
}
return list.get(head);
}
}
891. 子序列宽度之和(难度:困难)
一、题目
一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。
给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大,请返回对 10^9 + 7 取余 后的结果。
子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。
二、示例
2.1> 示例 1:
【输入】nums = [2,1,3]
【输出】6
【解释】子序列为 [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3] 。相应的宽度是 0, 0, 0, 1, 1, 2, 2 。宽度之和是 6 。
2.2> 示例 2:
【输入】nums = [2]
【输出】0
提示:
1<= nums.length <=10^51<= nums[i] <=10^5
三、解题思路
根据题目描述我们可以知道,计算宽度的时候,只是需要计算序列中【最大元素】与【最小元素】的差值。那么我们只需要关心数组nums中数字的大小而不需要关心它所在的位置了,即:子序列[2,4]与子序列[4,2]差值都是2。
那么,既然我们主要关注点是数组nums中的每个元素值大小,为了方便计算,我们首先可以对nums进行排序操作。
排序之后,我们随便找一个下标为i的元素,可以找到以下规律:
下面是以nums[i]和其左侧所有元素拼装组合成的子序列示例:
那么,针对于nums[i]来说,它的宽度总和就是: (2i-2nums.length-i-1) * nums[i] ,那么我们遍历所有nums数组中的元素,逐一计算每个元素的宽度总和,那么最终结果就是本题的答案。
四、代码实现
class Solution {
public int sumSubseqWidths(int[] nums) {
Arrays.sort(nums); // 排序
int mod = (int)1e9 + 7, n = nums.length;
long result = 0;
long[] pow = new long[n];
pow[0] = 1;
for (int i = 1; i < n; i++)
pow[i] = (pow[i - 1] << 1) % mod; // 初始化2^n的值
for (int i = 0; i < n; i++)
result = (result + (pow[i] - pow[n-i-1]) * nums[i] % mod) % mod; // 计算总和
return (int)result;
}
}
今天的文章内容就这些了:
写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。
更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(^o^)/ ~ 「干货分享,每天更新」