第十九天
884 两句话中的不常见单词
句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。
如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的 。
给你两个 句子s1
和 s2
,返回所有 不常用单词 的列表。返回列表中单词可以按 任意顺序 组织。
方法 哈希表
首先将两个句子连接起来,然后用空格进行分割,建立一个map
用于保存每个单词出现的次数,如果单词出现的次数为一次说明是不常见单词。
class Solution {
public String[] uncommonFromSentences(String s1, String s2) {
String s = s1 + " " + s2;
String[] strings = s.split(" ");
ArrayList<String> resArray = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (String str : strings) {
if (map.containsKey(str))
map.put(str, map.get(str) + 1);
else map.put(str, 1);
}
for (String key : map.keySet()) {
if (map.get(key) == 1) resArray.add(key);
}
String[] res = new String[resArray.size()];
int index = 0;
for (String i : resArray) res[index++] = i;
return res;
}
}
438 找到字符串中所有的的字母异位词
给定两个字符串 s
和p
,找到 s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
方法
使用一个differ
变量记录两个字符串中不不符合要求的字符的数量,一个count
数组用于记录每一个字符出现的次数。初始化时,对于第一个字符串,让其增加相应字符的数量,对于第二个字符串,让其减少相应字符串的数量。然后使用两个指针start
和end
,用于标记长度为第二个字符串的长度的区间,start
代表下一个要移出的位置,end
代表下一个要移进的位置。存在以下几种情况:
- 如果在
end
位置所在的字符移进之前,其count
值为0
,当其被移进之后,由于对于移进操作,count
值要加上1
,故当该字符被移进之后,count
值将不为0
,此时应当将differ
加1
,表示差异变大了。 - 如果在
end
位置被移进之后,其count
值为0
,说明差异变小了,此时differ
应当减1
。 - 对于移出的在
start
位置上字符,逻辑相同。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
int length1 = s.length();
int length2 = p.length();
if (length2 > length1) return res;
int[] count = new int[26];
for (int i = 0; i < length2; ++i){
++count[s.charAt(i) - 'a'];
--count[p.charAt(i) - 'a'];
}
int differ = 0;
for (int i : count) if (i != 0) ++differ;
if (differ == 0) res.add(0);
for (int end = length2; end < length1; ++end){//end是将要被移进的位置
int start = end - length2;//将要被移出的位置
if (count[s.charAt(end) - 'a'] == 0) ++differ;//第一种情况
count[s.charAt(end) - 'a']++;
if (count[s.charAt(end) - 'a'] == 0) --differ;//第二种情况
if (count[s.charAt(start) - 'a'] == 0) ++differ;//同理
count[s.charAt(start) - 'a']--;
if (count[s.charAt(start) - 'a'] == 0) --differ;
if (differ == 0) res.add(start + 1);
}
return res;
}
}
713 乘积小于K的子数组
给定一个正整数数组 nums
和整数 k
。
请找出该数组内乘积小于 k
的连续的子数组的个数。
方法一 暴力运算
遍历区间的长度,然后不断移动整个区间,如果符合条件,答案加1
。考虑到连乘可能会溢出,因此中间结果使用long
型变量保存。
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int res = 0;
long mulResRegister = 1;//寄存中间结果,避免重复运算
boolean flag = false;
int judge = 1;
//特判 如果所有元素相乘都没有超过目标值,那么直接返回答案即可
for (int i : nums) {
judge *= i;
if (judge >= k){
flag = true;
break;
}
}
if (!flag) return nums.length * (nums.length + 1) / 2;
for (int subLen = 1; subLen <= nums.length; ++subLen){
flag = false;
int start = 0, end = start + subLen;
long multiple = mulResRegister * nums[end - 1];
if (multiple < k) {res++;flag = true;}
mulResRegister = multiple;
while (end < nums.length){
start = end - subLen;
multiple = multiple / nums[start] * nums[end];
if (multiple < k) {
res++;
flag = true;
}
end++;
}
if (!flag) return res;//剪枝 如果对于当前长度的所有区间都不满足 这说明长度更大的区间也不满足,直接返回结果
}
return res;
}
}
方法二 双指针
定义以下两个指针l
和r
,r
指针从头开始,一直往后遍历,使用一个multiple
保存累乘结果,当r
指针向右移动时,同时让l
指针移动到第一个让总的multile
结果小于k
的位置,因为l
是最小的l
,因此,在这个l
到r
的区间内,所有连续的子区间都满足要求,一共有r-l
个子区间,一直让r
往右走,直到整个数组的末尾,同时累加满足要求的答案即可。
tips:关于l
不需要每次从0
开始往右移动的合理性在于,因为r
是一直往右移动的,那么[0,r]
区间的乘积一定会越来越大,此时应当引入一个左指针l
,当[0,r]
区间已经超过了目标值的时候,让左指针l
来缩小区间的搜索范围。如果l
在 通过上述方法得到的l
的左边,此时的区间乘积一定会超过k
,从而使得答案不满足要求。
时间复杂度:l
指针最多运动到数组的末尾,复杂度为O(n+n)=O(n)
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int res = 0;
int l = 0, r = 0;
int multiple = 1;
while (r < nums.length){
multiple = multiple * nums[r++];
while (l < r && multiple >= k) multiple /= nums[l++];
if (multiple < k) res += r - l;
}
return res;
}
}
209 长度最小的子数组
给定一个含有n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和≥ target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
方法 双指针
类比上一题,使用两个指针l
和r
。r
指针一直往后遍历,每次移动让和sum
加上nums[r]
,l
指针用于记录满足条件的最大的左区间指针,每次l
移动,让sum
减去nums[l]
。r
指针不断往右移动,每次循环更新一次答案,判断是否满足条件然后记录r-l
的最小值即可。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int res = Integer.MAX_VALUE;
int l = 0, r = 0, sum = 0;
while (r < nums.length){
sum += nums[r++];
while (l < r){
if (sum - nums[l] < target) break;
else sum -= nums[l++];
}
if (sum >= target) res = Math.min(res, r - l);
}
return res == Integer.MAX_VALUE ? 0 : res;
}
}