1.一维前缀和【模板】

暴力解法的时间复杂度为
前缀和:快速求出数组中某一个连续区间的和,时间复杂度是
step1:预处理出一个前缀和数组

dp[i]表示[1,i]区间内所有元素的和,dp无需遍历求和,dp[i]=dp[i-1]+arr[i],dp[0]=0;
索引从1开始是为了避免处理边界情况
step2:使用前缀和数组
求[l,r]之间元素的和,用dp[r]-dp[l-1]

代码如下:
cpp
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int n,m;
int l,r;
cin>>n>>m;
vector<int> arr(n+1);
for(int i=1;i<n+1;i++)
{
cin>>arr[i];
}
vector<long long> dp(n+1);//防止溢出
for(int i=1;i<n+1;i++)
{
dp[i]=dp[i-1]+arr[i];
}
while(m)
{
cin>>l>>r;
cout<<dp[r]-dp[l-1]<<endl;
m--;
}
return 0;
}
2.二维前缀和【模板】

暴力解法的时间复杂度是
前缀和的时间复杂度是
step1预处理出一个前缀和矩阵:
d[i][j]表示从[1,1]到[i,j]这段区间内所有元素的和

dp[i][j]=A+B+C+D=++A+B++ +++A+C+++D-A=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1]
step2使用前缀和矩阵
x1,y1\]\~\[x2,y2

D=A+B+C+D-(A+B)-(A+C)+A=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]
代码如下:
cpp
#include <iostream>
using namespace std;
#include <vector>
int main()
{
int n,m,q;
cin>>n>>m>>q;
int x1,y1,x2,y2;
vector<vector<long long>> arr(n+1,vector<long long>(m+1));
vector<vector<long long>> dp(n+1,vector<long long>(m+1));
for(int i=1;i<n+1;i++)
{
for(int j=1;j<m+1;j++)
{
cin>>arr[i][j];
}
}
for(int i=1;i<n+1;i++)
{
for(int j=1;j<m+1;j++)
{
dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+arr[i][j];
}
}
while(q)
{
cin>>x1>>y1>>x2>>y2;
cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
q--;
}
return 0;
}
3.寻找数组的中心下标


f:前缀和数组f[i]表示[0,i-1]区间内所有元素的和 f[i]=f[i-1]+nums[i-1]
g:后缀和数组:g[i]表示[i+1,n-1]区间所有元素的和g[i]=g[i+1]+nums[i+1]
细节问题:f(0)=0,从左向右,g(n-1)=0,从右向左
代码如下:
cpp
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
int n=nums.size();
vector<int> f(n),g(n);
for(int i=1;i<n;i++)
{
f[i]=f[i-1]+nums[i-1];
}
for(int i=n-2;i>=0;i--)
{
g[i]=g[i+1]+nums[i+1];
}
for(int i=0;i<n;i++)
{
if(f[i]==g[i])
{
return i;
}
}
return -1;
}
};
还有另一种方法:
cpp
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
int n=nums.size();
vector<int> dp(n+1);
for(int i=1;i<n+1;i++)
{
dp[i]=dp[i-1]+nums[i-1];
}
for(int i=1;i<n+1;i++)
{
if(dp[i-1]==(dp[n]-dp[i]))
{
return i-1;
}
}
return -1;
}
};
4.除自身以外数组的乘积
238. 除了自身以外数组的乘积 - 力扣(LeetCode)

f[i]表示[0,i-1]区间内所有元素的乘积,f[i]=f[i-1]*nums[i-1],f[0]=1 ,g[i]表示[i+1,n-1]区间内所有元素的乘积,g[i]=g[i+1]*nums[i+1],g[n-1]=1
代码如下:
cpp
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
int n=nums.size();
vector<int> f(n),g(n),anwer(n);
f[0]=1,g[n-1]=1;
for(int i=1;i<n;i++)
{
f[i]=f[i-1]*nums[i-1];
}
for(int i=n-2;i>=0;i--)
{
g[i]=g[i+1]*nums[i+1];
}
for(int i=0;i<n;i++)
{
anwer[i]=f[i]*g[i];
}
return anwer;
}
};
5.和为K的子数组

不能用滑动窗口,因为这里的数组元素包括0和负数,找到一个区间,而right不往后退
,若中间还有一个区间会被漏掉。
要在以i位置为结尾的所有子数组中找和为K的,即在[0,i-1]区间内,有多少个前缀和等于sum[i]-k(用哈希表储存<int,int>),如果sum[i]-k==0,就会去找前缀和为0的数组,但[0,-1]区间不存在,所以要提前把hash[0]=1;
用一个变量sum来标记前一个位置的前缀和。
如果sum-k这个值在哈希表中存在,说明之前有hash[sum-k]个前缀和等于sum-k。
代码如下:
cpp
class Solution {
public:
int subarraySum(vector<int>& nums, int k)
{
int n=nums.size();
unordered_map<int,int> hash;
hash[0]=1;
int sum=0,ret=0;
for(auto x:nums)
{
sum+=x;
if(hash.count(sum-k)) ret+=hash[sum-k];
hash[sum]++;
}
return ret;
}
};
6.和可被K整除的子数组
974. 和可被 K 整除的子数组 - 力扣(LeetCode)

①同余定理:
(a-b)/p=k......0->a%p=b%p
(a-b)/p=k a=b+pk a%p=(b+pk)%p=b%p
②负数%正数的结果及修正:负%正=负 a%p+p=(a%p+p)%p
如果不修正,会认为4%5和-1%5是两个不同的余数,实际上是模5共余的(-1+5=4)
(sum-x)%k=0->sum%k=x%k,sum%k要修正为(sum%k+k)%k
代码如下:
cpp
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
int sum=0,ret=0;
hash[0%k]=1;
for(auto x:nums)
{
sum+=x;
int r=(sum%k+k)%k;
if(hash.count(r)) ret+=hash[r];
hash[r]++;
}
return ret;
}
};
7.连续数组

可以把所有0改成-1,在数组中找出最长的子数组,使子数组中所有元素的和为0

如果前一个已经是sum[i],后一个必定短于前一个,不用记录。
每次循环(每到一个i),检查一下哈希表中是否有此前缀和,如果有,更新结果,且不用更新哈希表中此前缀和的下标,如果没有,更新哈希表中此前缀和的下标
sum[i]的最大范围是[0,i-1],当它为[0,-1]时情况也应是存在的,所以手动给hash[0]=-1;

len=i-下标
代码如下:
cpp
class Solution {
public:
int findMaxLength(vector<int>& nums)
{
unordered_map<int,int> hash;
int ret=0,sum=0;
hash[0]=-1;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i]==0?-1:1;
if(hash.count(sum)) ret=max(ret,i-hash[sum]);
else hash[sum]=i;
}
return ret;
}
};
8.矩阵区域和

首先需要一个dp数组,填充时注意下标的映射关系。

但经过k的扩展之后有可能越出m×n矩阵,题目要求的是(x1,y1)和(x2,y2)之间元素的和,且这些元素都要在mat内。

+1是为了在使用时满足映射关系。
代码如下:
cpp
class Solution
{
public:
vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
{
int m=mat.size(),n=mat[0].size();
vector<vector<int>> dp(m+1,vector<int>(n+1));
vector<vector<int>> answer(m,vector<int>(n));
for(int i=1;i<m+1;i++)
for(int j=1;j<n+1;j++)
{
dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+mat[i-1][j-1];
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
int x1=max(0,i-k)+1;
int y1=max(0,j-k)+1;
int x2=min(m-1,i+k)+1;
int y2=min(n-1,j+k)+1;
answer[i][j]=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];
}
return answer;
}
};