【剑指offer刷题记录 java版】数组双指针 之 滑动窗口

本系列文章记录labuladong的算法小抄中剑指offer题目



剑指 Offer 48. 最长不含重复字符的子字符串

题目链接:https://leetcode.cn/problems/zui-chang-bu-han-zhong-fu-zi-fu-de-zi-zi-fu-chuan-lcof/
在这里插入图片描述

class Solution {
    public int lengthOfLongestSubstring(String s) {
        Map<Character, Integer> map = new HashMap();
        int left=0,right=0;
        int length=0;
        while(right<s.length()){
            char c = s.charAt(right);
            map.put(c,map.getOrDefault(c,0)+1);// 进行窗口内数据的一系列更新
            right++;
            while(map.get(c)>1){// 判断左侧窗口是否要收缩
                char temp = s.charAt(left);
                map.put(temp, map.get(temp)-1);
                left++;
            }
            length = Math.max(length,right-left);
        }
        return length;
    }
}

剑指 Offer II 014. 字符串中的变位词

题目链接:https://leetcode.cn/problems/MPnaiL/
在这里插入图片描述

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        //map记录s1中包含的字母及数量
        Map<Character,Integer> map = new HashMap<>();
        for(int i=0;i<s1.length();i++){
            map.put(s1.charAt(i),map.getOrDefault(s1.charAt(i),0)+1);
        }

        //map2记录s2中,曾出现在s1中的字母及数量
        Map<Character,Integer> map2 = new HashMap<>();
        int left=0, right=0;
        int valid=0;//valid记录数量相同的字母的个数
        while(right<s2.length()){
            char c = s2.charAt(right);
            if(map.containsKey(c)){
                map2.put(c,map2.getOrDefault(c,0)+1);
                if (map2.get(c).equals(map.get(c)))
                    valid++;
            }
            right++;

            while(right-left==s1.length()){
                // 在这里判断是否找到了合法的子串
                if (valid == map.size())
                    return true;
                char d = s2.charAt(left);
                // 进行窗口内数据的一系列更新
                if (map.containsKey(d)) {
                    if (map2.get(d).equals(map.get(d)))
                        valid--;
                    map2.put(d, map2.get(d) - 1);
                }
                left++;
            }
        }
        return false;
    }
}

//因为只有26个小写英文字母,可以使用数组记录
class Solution {
    public boolean checkInclusion(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        if (n > m) {
            return false;
        }
        int[] cnt = new int[26];
        for (int i = 0; i < n; ++i) {
            --cnt[s1.charAt(i) - 'a'];
            ++cnt[s2.charAt(i) - 'a'];
        }
        int diff = 0;
        for (int c : cnt) {
            if (c != 0) {
                ++diff;
            }
        }
        if (diff == 0) {
            return true;
        }
        for (int i = n; i < m; ++i) {
            int x = s2.charAt(i) - 'a', y = s2.charAt(i - n) - 'a';
            // 滑窗右边扩张左边收缩
            if (x == y) continue;
            if (cnt[x] == 0) {++diff;}
            ++cnt[x];
            if (cnt[y] == 0) {++diff;}
            --cnt[y];

            // 根据现状判断有几处不同
            if (cnt[x] == 0) {--diff;}
            if (cnt[y] == 0) {--diff;}
            if (diff == 0) {return true;}
        }
        return false;
    }
}

剑指 Offer II 015. 字符串中的所有变位词

题目链接:https://leetcode.cn/problems/VabMRr/
在这里插入图片描述

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int m=s.length(), n=p.length();
        if(m<n){
            return new ArrayList<Integer>();
        }

        List<Integer> res = new ArrayList<>();
        int[] count = new int[26];
        for(int i=0;i<p.length();i++){
            count[p.charAt(i)-'a']--;
            count[s.charAt(i)-'a']++;
        }

        int diff=0;
        for(int num: count){
            if(num!=0) diff++;
        }
        

        if(diff==0){
            res.add(0);
        }

        for(int i=n; i<m;i++){
            //窗口下标范围为 [i-n, i)
            int index1 = s.charAt(i)-'a';//右边元素
            int index2 = s.charAt(i-n)-'a';//左边元素
            
            if(count[index1]==0) diff++;
            count[index1]++;
            if(count[index1]==0) diff--;

            if(count[index2]==0) diff++;
            count[index2]--;
            if(count[index2]==0) diff--;

            if(diff==0){
                res.add(i-n+1);
            }
        }
        return res;
    }
}

剑指 Offer II 016. 不含重复字符的最长子字符串

题目链接:https://leetcode.cn/problems/wtcaE1/
在这里插入图片描述

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if(s=="")return 0;
        int left=0,right=0;
        int length=0;
        // 使用哈希表记录窗口内字符出现次数
        Map<Character, Integer> map = new HashMap<>();
        while(right<s.length()){
            char c = s.charAt(right);
            right++;
            map.put(c, map.getOrDefault(c,0)+1);

            //如果窗口中包含c
            while(map.get(c)>=2){
                char temp = s.charAt(left);
                map.put(temp, map.get(temp)-1);
                left++;
            }
            length=Math.max(length,right-left);
        }
        return length;
    }
}

剑指 Offer II 017. 含有所有字符的最短字符串(难)

题目链接:https://leetcode.cn/problems/M1oyTv/
截图
进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?

class Solution {
    Map<Character, Integer> ori = new HashMap<Character, Integer>();
    Map<Character, Integer> cnt = new HashMap<Character, Integer>();

    public String minWindow(String s, String t) {
        int tLen = t.length();
        for (int i = 0; i < tLen; i++) {
            char c = t.charAt(i);
            ori.put(c, ori.getOrDefault(c, 0) + 1);
        }
        int l = 0, r = -1;
        int len = Integer.MAX_VALUE, ansL = -1, ansR = -1;
        int sLen = s.length();
        while (r < sLen) {
            ++r;
            if (r < sLen && ori.containsKey(s.charAt(r))) {
                cnt.put(s.charAt(r), cnt.getOrDefault(s.charAt(r), 0) + 1);
            }
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                    ansR = l + len;
                }
                if (ori.containsKey(s.charAt(l))) {
                    cnt.put(s.charAt(l), cnt.getOrDefault(s.charAt(l), 0) - 1);
                }
                ++l;
            }
        }
        return ansL == -1 ? "" : s.substring(ansL, ansR);
    }

    public boolean check() {
        Iterator iter = ori.entrySet().iterator(); 
        while (iter.hasNext()) { 
            Map.Entry entry = (Map.Entry) iter.next(); 
            Character key = (Character) entry.getKey(); 
            Integer val = (Integer) entry.getValue(); 
            if (cnt.getOrDefault(key, 0) < val) {
                return false;
            }
        } 
        return true;
    }
}

剑指 Offer 30. 包含min函数的栈

题目链接:https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof/
在这里插入图片描述

class MinStack {
    Deque<Integer> dq;
    Deque<Integer> minDq;

    /** initialize your data structure here. */
    public MinStack() {
        dq = new LinkedList<>();
        minDq = new LinkedList();
        minDq.offerLast(Integer.MAX_VALUE);//注意为了防止空栈,要在栈底添加最大元素
    }
    
    public void push(int x) {
        dq.offerLast(x);
        minDq.offerLast(Math.min(x,minDq.peekLast()));
    }
    
    public void pop() {
        dq.pollLast();
        minDq.pollLast();
    }
    
    public int top() {
        return dq.peekLast();
    }
    
    public int min() {
        return minDq.peekLast();
    }
}

剑指 Offer 59 - I. 滑动窗口的最大值(难)

题目链接:https://leetcode.cn/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/
截图
单调队列

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        Deque<Integer> deque = new LinkedList<Integer>();
        for (int i = 0; i < k; ++i) {
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.offerLast(i);
        }

        int[] ans = new int[n - k + 1];
        ans[0] = nums[deque.peekFirst()];
        for (int i = k; i < n; ++i) {
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.offerLast(i);
            while (deque.peekFirst() <= i - k) {
                deque.pollFirst();
            }
            ans[i - k + 1] = nums[deque.peekFirst()];
        }
        return ans;
    }
}

总结

滑动窗口要先扩张后收缩,即先移动右边指针,再移动左边指针。
固定大小的窗口使用for循环遍历,窗口大小不固定时使用while遍历。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值