前綴和 (差分)算法

什么是前綴和?前綴和是一個(gè)數(shù)組的某項(xiàng)下標(biāo)之前(包括此項(xiàng)元素)的所有數(shù)組元素的和。

設(shè) b[] 為前綴和數(shù)組,a[] 為原數(shù)組,根據(jù)這句話可以得到前綴和的定義式和遞推式:

定義式 遞推式
一維前綴和 b[i] = \sum_{j=0}^{i}a[j] b[i] = b[i-1] + a[i]
二維前綴和 b[x][y] = \sum_{i=0}^{x}\sum_{j=0}^{y}a[i][j] b[x][y] = b[x-1][y] + b[x][y-1] - b[x-1][y-1] + b[x][y]

303. 區(qū)域和檢索 - 數(shù)組不可變

題目描述:

給定一個(gè)整數(shù)數(shù)組 nums,求出數(shù)組從索引 i 到 j (i ≤ j) 范圍內(nèi)元素的總和,包含 i, j 兩點(diǎn)。 

示例: 
給定 nums = [-2, 0, 3, -5, 2, -1],求和函數(shù)為 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3 

說明: 
你可以假設(shè)數(shù)組不可變。 
會(huì)多次調(diào)用 sumRange 方法。 

Related Topics 動(dòng)態(tài)規(guī)劃

非?;A(chǔ)的前綴和算法,我們先計(jì)算并保存前綴和數(shù)組,檢索的時(shí)候直接調(diào)用即可:

class NumArray:

    def __init__(self, nums):
        """
        :param nums: List[int]
        """
        self.nums = nums
        self.cumSums = [0]
        for num in self.nums:
            self.cumSums.append(self.cumSums[-1]+num)

    def sumRange(self, i: int, j: int) -> int:
        return self.cumSums[j+1] - self.cumSums[i]

304. 二維區(qū)域和檢索 - 矩陣不可變

題目描述:

給定一個(gè)二維矩陣,計(jì)算其子矩形范圍內(nèi)元素的總和,該子矩陣的左上角為 (row1, col1) ,
右下角為 (row2, col2)。 

上圖子矩陣左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),該子矩形內(nèi)元素的總和為 8。 

示例: 
給定 matrix = [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]

sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12

說明: 
你可以假設(shè)矩陣不可變。 
會(huì)多次調(diào)用 sumRegion 方法。 
你可以假設(shè) row1 ≤ row2 且 col1 ≤ col2。 

Related Topics 動(dòng)態(tài)規(guī)劃

這又是非?;A(chǔ)的二維前綴和算法,我們先計(jì)算二維的前綴和數(shù)組并保存,檢索的時(shí)候直接調(diào)用結(jié)果:

import numpy as np
class NumMatrix:

    def __init__(self, matrix):
        """
        前綴和
        :param matrix: List[List[int]]
        """
        self.matrix = np.array(matrix)
        if self.matrix.size == 0:
            return
        # 計(jì)算前綴和
        rows, cols = self.matrix.shape
        self.cumSums = np.zeros((rows+1, cols+1), dtype=int)
        for i in range(rows):
            for j in range(cols):
                self.cumSums[i+1, j+1] = self.cumSums[i, j+1] + self.cumSums[i+1, j] \
                                         - self.cumSums[i, j] + self.matrix[i, j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return self.cumSums[row2+1, col2+1] - self.cumSums[row1, col2+1] - \
               self.cumSums[row2+1, col1] + self.cumSums[row1, col1]

525. 連續(xù)數(shù)組

題目描述:

給定一個(gè)二進(jìn)制數(shù)組, 找到含有相同數(shù)量的 0 和 1 的最長連續(xù)子數(shù)組(的長度)。 

示例 1: 
輸入: [0,1]
輸出: 2
說明: [0, 1] 是具有相同數(shù)量0和1的最長連續(xù)子數(shù)組。 

示例 2: 
輸入: [0,1,0]
輸出: 2
說明: [0, 1] (或 [1, 0]) 是具有相同數(shù)量0和1的最長連續(xù)子數(shù)組。 

注意: 給定的二進(jìn)制數(shù)組的長度不會(huì)超過50000。 
Related Topics 哈希表

首先想到的是暴力解法,遍歷每個(gè)子區(qū)間,統(tǒng)計(jì) 01 的數(shù)量,如果 01 的數(shù)量相等,再更新最大長度,這樣的時(shí)間復(fù)雜度為 O(n^3);我們可以在這個(gè)基礎(chǔ)上進(jìn)行優(yōu)化,在外層循環(huán)內(nèi)定義兩個(gè)變量 zerosones 來記錄 01 的數(shù)量,這樣可以將時(shí)間復(fù)雜度降低至 O(n^2)。

class Solution:
    def findMaxLength(self, nums: List[int]) -> int:
        if nums is None or len(nums) <= 1:
            return 0
        maxlen = 0
        for i in range(len(nums)):
            zeros, ones = 0, 0
            for j in range(i, len(nums)):
                if nums[j] == 0:
                    zeros += 1
                else:
                    ones += 1
                if zeros == ones:
                    maxlen = max(maxlen, j-i+1)
        return maxlen

當(dāng)我們欣喜的點(diǎn)擊提交后,缺遲遲未得到反饋結(jié)果,終于:超出時(shí)間限制
想想 O(n^2) 的時(shí)間復(fù)雜度,確實(shí)不夠高效。那又該從何開始優(yōu)化呢?


仔細(xì)分析上面的解法,如上圖,我們要求的子區(qū)間為 , 為整個(gè)區(qū)間的起點(diǎn), 為要求的子區(qū)間的起點(diǎn), 為要求的子區(qū)間的終點(diǎn)。我們依次固定 點(diǎn),然后遍歷 、計(jì)數(shù)、判斷、更新結(jié)果。由于 在 之間,我們每次循環(huán)都要重復(fù)遍歷一遍 的某個(gè)子區(qū)間,那有沒有辦法通過一次遍歷就得到結(jié)果呢?一次遍歷,那肯定需要記錄多個(gè)結(jié)果,所以肯定要用到哈希表。我們再來看哈希表記錄什么,由于子區(qū)間 中 01 數(shù)量相等,假如我們將 0 改為 -1,那區(qū)間 之間 0(-1)1 求和恰好為 0,也就是說 之間 0(-1)1 求和的結(jié)果和 之間 0(-1)1 求和的結(jié)果是相同的!也就是說當(dāng)我們遇到兩個(gè)相同求和結(jié)果的時(shí)候,便是滿足 01 相等的時(shí)候,此時(shí)的區(qū)間也就是滿足條件的一個(gè)候選區(qū)間,那該如何計(jì)算區(qū)間的長度呢?我們只需要知道上一次該求和結(jié)果的位置。所以哈希表需要記錄的數(shù)據(jù)便也清楚了:求和結(jié)果為鍵,上一次的位置為值。
但是仔細(xì)一思考發(fā)現(xiàn),題目要求的是最長的子區(qū)間,所以我們哈希表記錄的值應(yīng)該為求和結(jié)果第一次出現(xiàn)的位置。

class Solution:
    def findMaxLength(self, nums: List[int]) -> int:
        if nums is None or len(nums) <= 1:
            return 0
        maxlen, count = 0, 0
        # 記錄該count下的第一個(gè)index
        hashmap = {0: -1}
        for i in range(len(nums)):
            count += 1 if nums[i] == 0 else -1
            if hashmap.get(count) is not None:
                maxlen = max(maxlen, i-hashmap.get(count))
            else:
                hashmap[count] = i
        return maxlen

1371. 每個(gè)元音包含偶數(shù)次的最長子字符串

題目描述:

給你一個(gè)字符串 s ,請你返回滿足以下條件的最長子字符串的長度:每個(gè)元音字母,
即 'a','e','i','o','u' ,在子字符串中都恰好出現(xiàn)了偶數(shù)次。

示例 1: 
輸入:s = "eleetminicoworoep"
輸出:13
解釋:最長子字符串是 "leetminicowor" ,它包含 e,i,o 各 2 個(gè),以及 0 個(gè) a,u 。

示例 2: 
輸入:s = "leetcodeisgreat"
輸出:5
解釋:最長子字符串是 "leetc" ,其中包含 2 個(gè) e 。

示例 3: 
輸入:s = "bcbcbc"
輸出:6
解釋:這個(gè)示例中,字符串 "bcbcbc" 本身就是最長的,因?yàn)樗械脑?a,e,i,o,u 都出現(xiàn)了 0 次。

提示: 
1 <= s.length <= 5 x 10^5 
s 只包含小寫英文字母。 
Related Topics 字符串

題目分析:
要求最長子字符串,也就是一個(gè)最長子區(qū)間,那和我們上一題類似。在上一題中,我們的 count 變量是記錄 01 多出來的次數(shù),當(dāng)區(qū)間 01 次數(shù)相等時(shí),count 變量還是同一個(gè)值。類比到此題,我們希望有一個(gè)變量能夠記錄元音字母 a, e, i, o, u 出現(xiàn)的次數(shù),并且希望其在元音字母恰好出現(xiàn)了偶數(shù)次的時(shí)候,這個(gè)變量的值能夠和上一次相等。
題目只是要求偶數(shù)次,偶數(shù),即是 x % 2 == 0,那我們便可以想到利用二進(jìn)制來存儲(chǔ):當(dāng)出現(xiàn)一次(奇數(shù)次)時(shí),我們記錄為 1,出現(xiàn)兩次(偶數(shù)次)時(shí),我們記錄為 0, 于是可以利用異或 ^ 來計(jì)算結(jié)果。由于要記錄5個(gè)元音字母,那我們可以用5個(gè)二進(jìn)制位來進(jìn)行記錄,所以這里的 count 范圍為 00000-11111。當(dāng)區(qū)間中元音字母都出現(xiàn)偶數(shù)次時(shí),count 還是同一個(gè)結(jié)果。(代碼中的 status 即為分析中的 count)

class Solution:
    def findTheLongestSubstring(self, s: str) -> int:
        # status 用來記錄元音字母出現(xiàn)的奇偶性組合
        maxlen, status = 0, 0
        # 記錄status第一次出現(xiàn)的位置
        hashmap = {0: -1}
        for i in range(len(s)):
            if s[i] == 'a':
                status ^= 1 << 0
            elif s[i] == 'e':
                status ^= 1 << 1
            elif s[i] == 'i':
                status ^= 1 << 2
            elif s[i] == 'o':
                status ^= 1 << 3
            elif s[i] == 'u':
                status ^= 1 << 4
            if hashmap.get(status) is not None:
                maxlen = max(maxlen, i-hashmap.get(status))
            else:
                hashmap[status] = i
        return maxlen
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容