hot100-一刷-04子串(共3道題)

chendsome發表於2024-12-03

560. 和為 K 的子陣列

題目連結

題目描述

image

程式碼實現

分析:

  • 暴力:還是有點技巧的,如果單純暴力,外層fori迴圈遍歷起始下標start,內層forj迴圈遍歷末尾end,裡面還需要個迴圈,計算從i加到j,最壞會到\(O(n^3)\)。考慮固定某一個邊界,比如固定end,從end往前算。

    點選檢視程式碼
    class Solution {
    	public int subarraySum(int[] nums, int k) {
    		int count = 0;
    		for (int i = 0; i < nums.length; i ++){
    			int sum = 0;
    			for (int j = i ; j >= 0; j--) {
    				// 暴力 從後累加, 可以用到前一次的累加結果,有點像dp揹包內迴圈遍歷順序的討論
    				sum += nums[j];
    				if (sum == k){
    					count++;
    				}
    			}
    		}
    
    		return count;
    	}
    }
    
  • 字首和,類似1.兩數之和

程式碼:

// 字首和
class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        Map<Integer, Integer> map = new HashMap<>();
        int len = nums.length;
        int[] preSum = new int[len + 1];

        preSum[0] = 0;
        for (int i = 0; i < len; i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
        for (int i = 0; i < nums.length; i++){
            // i 為0的時候, 就是計算 字首子陣列相加就是k的情況。
            // 字首子陣列的意思就是 從陣列最開始到j的位置。[0,j]
            for(int j = i; j < nums.length; j++){
                // [i,j] 相加為k
                if(preSum[j+1] - preSum[i] == k){
                    count++;
                }
            }
        }
        return count;
    }


// 字首和 + 雜湊表 
class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        Map<Integer, Integer> map = new HashMap<>();
        int sum = 0;
        map.put(0,1); // 初始化字首和為0的次數為1, 也是一樣的到裡,當字首子陣列和為k的時候,需要計數1次
        for (int i = 0; i < nums.length; i++){
            // 這裡的i相當於是 字首和 解法中的j, 是右邊界,sum - (sum - k) == k, 只需要在遍歷到sum的時候
            // 看看此時的sum-k裡的個數就行,也就是[left, i]中left的個數,在遍歷的過程中,map裡記錄了left的個數
            sum += nums[i];
            if (map.containsKey(sum - k)) {
                count += map.get(sum - k);
            }
            map.put(sum, map.getOrDefault(sum, 0) + 1);
        }
        return count;
    }
}

239. 滑動視窗最大值

題目連結

題目描述

image

程式碼實現

分析:

  • 使用一個單調佇列完成,Deque來模擬單調佇列。佇列的順序是從大到小。每次滑動視窗的過程可以分解為兩個步驟:
    • 左邊界元素移出:移出時只需要判斷一下是否是當前佇列前端元素(最大值),如果是就移出佇列。不是就說明移除的元素不是當前視窗的最大值,不做操作。
    • 新增元素:新增元素時要判斷加進來的元素與單調佇列中的大小關係,按照約定的順序,從佇列尾部一個一個比較,佇列尾部開始,比它小的元素都彈出,直到遇到大於等於這個新進來的元素時,加入隊尾。

程式碼:

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        Deque<Integer> stack = new LinkedList<>();
        int n = nums.length;
        // 初始化單調棧 棧頭->棧底: 大->小  小的並不是完全不要,而是捨去比新加進來的元素小的元素
        for(int i = 0; i < k; i++){
            while(!stack.isEmpty() && nums[i] > stack.peekLast()){
                stack.pollLast();
            }
            stack.offerLast(nums[i]);
        }
        int[] res = new int[n-k+1];
        res[0] = stack.peekFirst();
        // 模擬滑動視窗
        for(int i = k; i < n; i++){
            // 視窗左邊界:當前需要移除視窗的元素
            if(nums[i-k] == stack.peekFirst()){
                stack.pollFirst();
            }
            // 視窗右邊新加進來的元素需要和棧內元素進行比較 這裡不能用>=,因為可能有重複元素
            while(!stack.isEmpty() && nums[i] > stack.peekLast()){
                stack.pollLast();
            }
            stack.offerLast(nums[i]);
            res[i-k+1] = stack.peekFirst();
        }
        return res;
    }
}

76. 最小覆蓋子串

題目連結

題目描述

image

程式碼實現

分析:

程式碼:


相關文章