力扣第35場雙週賽
這場比賽是建行贊助的,不得不說,國企就是有錢,已經霸佔雙週賽兩三個月了,貌似還會繼續,nb!!!
不多說了,看題:
思路一:直接暴力,列舉所有奇數長度的子陣列即可,用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 ++)View Code7 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 };
思路二:既然是連續子序列的和,很容易想到字首和。可以先計算字首和陣列,之後使用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++)View Codefor (int j = i; j < arr.length; j +=2) { if(i == 0) ans += preSum[j]; else ans += preSum[j] - preSum[i - 1]; } return ans; } }
思路三:計算所有奇數長度的子陣列和--->換成計算每個數字所需要加的次數來得出最終結果。
對於任意元素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 題意:給一個數組,元素順序可以打亂,根據給出的區間計算所有區間內元素最大和。 思路:這個題跟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
題意:將給定陣列的一部分移除,使得剩餘元素和能被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