1. 程式人生 > 實用技巧 >力扣第35場雙週賽

力扣第35場雙週賽

這場比賽是建行贊助的,不得不說,國企就是有錢,已經霸佔雙週賽兩三個月了,貌似還會繼續,nb!!!

不多說了,看題:

1、5503. 所有奇數長度子陣列的和

思路一:直接暴力,列舉所有奇數長度的子陣列即可,用O(n^3)的時間複雜度也能過。

程式碼(copy大佬的C++碼):

 1 class Solution {
 2 public:
 3     int sumOddLengthSubarrays(vector<int>& arr) {
 4 
 5         int res = 0;
 6         for(int i = 0; i < arr.size(); i ++)
7 for(int sz = 1; i + sz - 1 < arr.size(); sz += 2) 8 res += accumulate(arr.begin() + i, arr.begin() + i + sz, 0); 9 //一個累加求和函式,頭兩個形參指定要累加的元素範圍,第三個形參則是累加的初值 10 return res; 11 } 12 };
View Code

思路二:既然是連續子序列的和,很容易想到字首和。可以先計算字首和陣列,之後使用O(1)的時間即可計算一個連續子陣列的和。總時間複雜度位O(n^2),空間複雜度O(n)。

程式碼(Java):

class Solution {
    public int sumOddLengthSubarrays(int[] arr) {
        int ans = 0;
        int[] preSum = new int[arr.length +5];
        preSum[0] = arr[0];
        for (int i = 1;i < arr.length; i++)
            preSum[i] = arr[i] + preSum[i-1];
        for (int i = 0;i < arr.length; i++) 
            
for (int j = i; j < arr.length; j +=2) { if(i == 0) ans += preSum[j]; else ans += preSum[j] - preSum[i - 1]; } return ans; } }
View Code

思路三:計算所有奇數長度的子陣列和--->換成計算每個數字所需要加的次數來得出最終結果。

對於任意元素i(下標),左邊的元素可以一次性取0~i個,共i+1種方案,其中(i+1)/2種為奇數,i/2+1種為偶數;

右邊的元素可以一次性取0 ~ n-i-1個,共n-i種方案,其中(n-i)/2種為奇數,(n-i+1)/2種為偶數;

所以左奇+右奇+本身或左偶+右偶+本身即 可得到連續奇陣列。所以arr[i]出現的次數=左奇*右奇+左偶*右偶。

程式碼(Java):

 1 public int sumOddLengthSubarrays(int[] arr) {
 2         int ans = 0;
 3         int l1 = 0,r1 = 0,l2 = 0,r2 = 0;
 4         for (int i = 0; i < arr.length; i++) {
 5             l1 = (i + 1) / 2;
 6             l2 = i / 2 + 1;
 7             r1 = (arr.length - i) / 2;
 8             r2 = (arr.length - i +1) / 2;
 9             ans += (l1*r1+l2*r2) *arr[i];
10         }
11         return ans;
12     }
View Code

2、5505. 所有排列中的最大和

題意:給一個數組,元素順序可以打亂,根據給出的區間計算所有區間內元素最大和。 思路:這個題跟1題的第三種方法有一丟丟像,我們需要統計每個索引位置的查詢次數,然後貪心:陣列元素越大,對應越多的查詢次數。 怎樣根據給出的不同區間統計查詢次數: 使用掃描線:對於每一個request[start, end],從start開始的數字查詢次數增加一次,從end+1開始的查詢係數減少一次,可以用一個cnt陣列,對每一個查詢區間,進行cnt[start]++、cnt[end + 1] --;得到的cnt陣列的每個元素的字首和就是這個元素的查詢次數。 程式碼(Java):
 public int maxSumRangeQuery(int[] nums, int[][] requests) {
        long sum = 0;
        int mod = 1000000007;
        int[] cnt = new int[nums.length + 5];//防止越界資料
        for (int i = 0; i < requests.length; i++) {
            cnt[requests[i][0]]++;
            cnt[requests[i][1]+1]--;
        }
        for (int i = 1; i < nums.length; i++)
            cnt[i] = cnt[i] + cnt[i - 1];
        Arrays.sort(nums);
        Arrays.sort(cnt, 0, nums.length);
        for (int i = nums.length - 1; i >= 0; i--)
            sum = (sum + nums[i] * cnt[i]) % mod;
        return (int)sum;
    }
View Code

3、5504. 使陣列和能被 P 整除

題意:將給定陣列的一部分移除,使得剩餘元素和能被p整除,不能移除全部元素。

思路:

假設 nums 的和除以 P,餘數是 mod,
如果 mod == 0,答案就是 0。
如果 mod != 0,答案變成了找原陣列中的最短連續子陣列,使得其數字和除以 P,餘數也是 mod。
由於是求解連續子陣列和的問題,很容易想到使用字首和。
假設當前字首和除以 P 的餘數是 curmod,為了找到一段連續子陣列對 P 的餘數是 mod,我們需要找到一段字首和,對 P 的餘數是 targetmod。其中 targetmod 的求法是:
如果 curmod >= mod,很簡單:targetmod = curmod - mod;
如果 curmod < mod,我們需要加上一個 P:targetmod = curmod - mod + P;
這樣,我們可以保證,當前字首和減去目標字首和,剩餘的陣列對 P 的餘數是 mod。我們只需要找最短的這樣的陣列就好。
最後,為了快速找到一段對 P 的餘數為 targetmod 的字首和,我們使用一個雜湊表 table,來儲存之前字首和對 P 的餘數和所在的索引。(key 為餘數;value 為索引)。table 在遍歷過程中更新,以保證每次在 table 中查詢到的,是離當前元素最近的索引,從而保證找到的是“最短”的連續子陣列。

程式碼:

public int minSubarray(int[] nums, int p) {
        int ans = nums.length;
        long sum = 0;
        for (int i: nums)
            sum += i;
        int mod = (int)(sum % p);
        if (mod == 0)
            return 0;
        HashMap<Integer,Integer> preSum = new HashMap<>();
        preSum.put(0, -1);
        sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            int currentMod = (int)(sum % p);
            preSum.put(currentMod, i);
            int tmp = currentMod >= mod ? currentMod - mod:(currentMod + p - mod);
            if(preSum.containsKey(tmp))
                ans = Math.min(ans, i-preSum.get(tmp));
        }
        return ans == nums.length ? -1:ans;
    }
View Code