0
点赞
收藏
分享

微信扫一扫

leetcode刷题第19天——884,438,713,209

第十九天

884 两句话中的不常见单词

句子 是一串由空格分隔的单词。每个 单词 仅由小写字母组成。

如果某个单词在其中一个句子中恰好出现一次,在另一个句子中却 没有出现 ,那么这个单词就是 不常见的

给你两个 句子s1s2 ,返回所有 不常用单词 的列表。返回列表中单词可以按 任意顺序 组织。

方法 哈希表

首先将两个句子连接起来,然后用空格进行分割,建立一个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 找到字符串中所有的的字母异位词

给定两个字符串 sp,找到 s中所有p异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。

方法

使用一个differ变量记录两个字符串中不不符合要求的字符的数量,一个count数组用于记录每一个字符出现的次数。初始化时,对于第一个字符串,让其增加相应字符的数量,对于第二个字符串,让其减少相应字符串的数量。然后使用两个指针startend,用于标记长度为第二个字符串的长度的区间,start代表下一个要移出的位置,end代表下一个要移进的位置。存在以下几种情况:

  • 如果在end位置所在的字符移进之前,其count值为0,当其被移进之后,由于对于移进操作,count值要加上1,故当该字符被移进之后,count值将不为0,此时应当将differ1,表示差异变大了。
  • 如果在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;
    }
}

方法二 双指针

定义以下两个指针lrr指针从头开始,一直往后遍历,使用一个multiple保存累乘结果,当r指针向右移动时,同时让l指针移动到第一个让总的multile结果小于k的位置,因为l是最小的l,因此,在这个lr的区间内,所有连续的子区间都满足要求,一共有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

方法 双指针

类比上一题,使用两个指针lrr指针一直往后遍历,每次移动让和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;
    }
}
举报

相关推荐

0 条评论