【雙指針】B021_LC_滿足條件的子序列數目(二分思想 + 排列組合 + 冪數組)

一、Problem

給你一個整數數組 nums 和一個整數 target 。

請你統計並返回 nums 中能滿足其最小元素與最大元素的 和 小於或等於 target 的 非空 子序列的數目。

由於答案可能很大,請將結果對 10^9 + 7 取餘後返回。

輸入:nums = [3,5,6,7], target = 9
輸出:4
解釋:有 4 個子序列滿足該條件。
[3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9)
[3,5] -> (3 + 5 <= 9)
[3,5,6] -> (3 + 6 <= 9)
[3,6] -> (3 + 6 <= 9)

提示:

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^6
1 <= target <= 10^6

二、Solution

方法一:雙指針

思路

題目的子序列字樣有點迷惑人,但是仔細想一下會發現其實可以不考慮一組數的順序,所以知道找到了一組合法的數,假設這組數的長度爲 k,那麼一共會有 2k12^{k-1},比如(a 是有序的數組):

a = [mi,n1,n2,ma],tar = x
若 mi+mx <= tar,則一定會有 mi + mi < tar;mi+n1 <= tar;mi+n2 < tar,所以以下的子序列都是合法的:
[mi],[mi,n1], [mi, n2], [mi, ma], [mi, n1, n2],  [mi, n1, ma], [mi, n2, ma], [mi,n1,n2,ma]
一共  8 個
class Solution {
    public int numSubseq(int[] a, int tar) {
    	Arrays.sort(a);
    	if (a[0]*2 > tar)
    		return 0;
    	int n = a.length, ans = 0, l = 0, r = n-1, mod = (int) 1e9+7;

    	while (l <= r) {
    		if (a[l] + a[r] > tar) {
    			r--;
    		} else {
    			ans = (ans + (int) Math.pow(2, r-l)) % mod;
    			l++;
    		}
    	}
    	return ans;
    }
}

溢出問題:當 r-l 很大時,會造成整形溢出,怎麼解決呢?比賽的時候我是換成了 python,事後發現有兩種方案可以解決問題:

  • 用快速冪 + 同餘來避免溢出
  • 利用同餘預處理一個長度爲 n 的2的冪次方數數組
class Solution {
    public int numSubseq(int[] a, int tar) {
    	Arrays.sort(a);
    	if (a[0]*2 > tar)
    		return 0;
    	int n = a.length, ans = 0, l = 0, r = n-1, mod = (int) 1e9+7, pow[] = new int[n];
        pow[0] = 1;
    	for (int i = 1; i < n; i++)
    		pow[i] = (pow[i-1] << 1) % mod;
        
    	while (l <= r) {
    		if (a[l] + a[r] > tar) {
    			r--;
    		} else {
    			ans = (ans + pow[r-l]) % mod;
    			l++;
    		}
    	}
    	return ans;
    }
}

複雜度分析

  • 時間複雜度:O(n)O(n)
  • 空間複雜度:O(n)O(n)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章