目录
- 知识框架
- LeetCode
- No.1 按照顺序的结果没坚持下来
- [1. 两数之和](https://leetcode.cn/problems/two-sum/)
- [3. 无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters/)
- [4. 寻找两个正序数组的中位数](https://leetcode.cn/problems/median-of-two-sorted-arrays/)
- [5. 最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring/)
- [6. N 字形变换](https://leetcode.cn/problems/zigzag-conversion/)
- [7. 整数反转](https://leetcode.cn/problems/reverse-integer/)
- No.2 动态规划
- No.3 周赛
- No.4 字符串
- No.5 前缀和
- 前缀和+哈希表
- 滑动窗口模板
- [209. 长度最小的子数组](https://leetcode.cn/problems/minimum-size-subarray-sum/):滑动窗口
- [238. 除自身以外数组的乘积](https://leetcode.cn/problems/product-of-array-except-self/) 前缀和+后缀和
- [1004. 最大连续1的个数 III](https://leetcode.cn/problems/max-consecutive-ones-iii/):滑动窗口
- [1124. 表现良好的最长时间段](https://leetcode.cn/problems/longest-well-performing-interval/):前缀和+单调栈
- [724. 寻找数组的中心下标](https://leetcode.cn/problems/find-pivot-index/):前缀和
- [560. 和为 K 的子数组](https://leetcode.cn/problems/subarray-sum-equals-k/):前缀和+哈希表
- [1248. 统计「优美子数组」](https://leetcode.cn/problems/count-number-of-nice-subarrays/):前缀和+哈希表
- [974. 和可被 K 整除的子数组](https://leetcode.cn/problems/subarray-sums-divisible-by-k/):前缀和+哈希表
- [523. 连续的子数组和](https://leetcode.cn/problems/continuous-subarray-sum/):前缀和+哈希表
- [930. 和相同的二元子数组](https://leetcode.cn/problems/binary-subarrays-with-sum/):前缀和+哈希表
- [904. 水果成篮](https://leetcode.cn/problems/fruit-into-baskets/):滑动窗口+哈希表
- [904. 水果成篮](https://leetcode.cn/problems/fruit-into-baskets/):滑动窗口+哈希表
知识框架
LeetCode
No.1 按照顺序的结果没坚持下来
1. 两数之和
主要是 哈希表进行优化;
重点是 map的find; it->second ;以及 it != hastable.end();
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int>hastable;
for(int i=0;i<nums.size();i++){
hastable[nums[i]]=i;
}
vector<int>res;
for(int i=0;i<nums.size();i++){
auto it = hastable.find(target-nums[i]);
if(it !=hastable.end()){
if(i>=it->second){
continue;
}
res.push_back(i);
res.push_back(it->second);
break;
}
}
return res;
}
};
3. 无重复字符的最长子串
滑动窗口:
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() == 0) return 0;
unordered_set<char> lookup;
int maxStr = 0;
int left = 0;
for(int i = 0; i < s.size(); i++){
while (lookup.find(s[i]) != lookup.end()){//找到的话;
lookup.erase(s[left]);
left ++;
}
maxStr = max(maxStr,i-left+1);
lookup.insert(s[i]);
}
return maxStr;
}
};
4. 寻找两个正序数组的中位数
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size();
int n = nums2.size();
vector<int>ans;
int pos1=0,pos2=0;
while(pos1<m&&pos2<n){
if(nums1[pos1]<=nums2[pos2]){
ans.push_back(nums1[pos1]);
pos1++;
}else{
ans.push_back(nums2[pos2]);
pos2++;
}
}
if(pos1>=m){
for( ; pos2<n;pos2++)ans.push_back(nums2[pos2]);
}
if(pos2>=n){
for( ; pos1<m;pos1++)ans.push_back(nums1[pos1]);
}
double res =0.0;
if((m+n)%2==0){
res=(ans[(m+n)/2-1]+ans[(m+n)/2])/2.0;
}else{
res=ans[(m+n-1)/2]*1.0;
}
return res;
}
};
5. 最长回文子串
中心扩散法子;
1、从每一个位置出发,向两边扩散即可。遇到不是回文的时候结束
首先往左寻找与当期位置相同的字符,直到遇到不相等为止。
然后往右寻找与当期位置相同的字符,直到遇到不相等为止。
最后左右双向扩散,直到左和右不相等。
为什么是这样呢??
如果长度是单数,那么很明显,最中心的是一个字符;而结果是偶数 的话,那么中心就是两个字符,并且相同。
class Solution {
public:
string longestPalindrome(string s) {
//中心扩散法:从每个字母开始 进行扩散遍历判断;
int n=s.size();
int left = 0;
int right = 0;
int len = 1; //记录选择的字符字串长度
int maxStart = 0;
int maxLen = 0;
for (int i = 0; i < n; i++) {
len=1;
left = i - 1;
right = i + 1;
while (left >= 0 && s[left]==s[i]) {
len++;
left--;
}
while (right < n && s[right]==s[i]) {
len++;
right++;
}
while (left >= 0 && right < n && s[left]==s[right]) {
len = len + 2;
left--;
right++;
}
if (len > maxLen) {
maxLen = len;
maxStart = left;
}
}
return s.substr(maxStart+1,maxLen);
}
};
6. N 字形变换
7. 整数反转
弹出最后的数字,
压入;
while循环进行倒转;;;
class Solution {
public:
int reverse(int x) {
char resStar='0';
if(x<0) resStar='-';
x=abs(x);
int temp=0;
int res=0;
while(x!=0){
temp=x%10;
x=x/10;
res=res*10+temp;
if(res>214748364){
return 0;
}
}
if(resStar=='-'){
return -res;
}else{
return res;
}
}
};
No.2 动态规划
5.最长回文串:
给你一个字符串 s
,找到 s
中最长的回文子串。
对于一个子串而言,如果它是回文串,并且长度大于2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串“ababa”,如果我们已经知道“bab”是回文串,那么“ababa”一定是回文串,这是因为它的首尾两个字母都是“a”。
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用P(i,j)表示字符串s的第i到j个字母组成的串 (下文表示成s[i:j])是否为回文串:P(i,j)一false,其它情况这里的「其它情况」包含两种可能性:. s[i,j]本身不是一个回文串;或者 i> j,此时s[i,j]本身不合法。
那么我们就可以写出动态规划的状态转移方程:
P(i,j)=P(i+1,j- 1)乘以(S:== S;)
也就是说,只有s[i+1 :j-1]是回文串,并且s 的第i和j个字母相同时,s[i :j才会是回文串。上文的所有讨论是建立在子串长度大于2的前提之上的,我们还需要考虑动态规划中的边界条件,即子串的长度为1或2。对于长度为1的子串,它显然是个回文串;对于长度为2的子串,只要它的两个字母相同,它就是一个回文串。因此我们就可以写出动态规划的边界条件:
P(i,i)= true
P(i,i+1)=(S== Si+1)
根据这个思路,我们就可以完成动态规划了,最终的答案即为所有P(i,j)= true中j-i+1(即子串长度)的最大值。注意:在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进行转移的,因此一定要注意动态规划的循环顺序。
#include<bits/stdc++.h>
using namespace std;
#define N 10001
int main()
{
int dp[N][N];
string s;
cin>>s;
if(s.size()<2){
//则本身为回文串;
}
int n=s.size();
int maxlen=1;
int begin=0;
// dp[i][j] 表示 s[i..j] 是否是回文串
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < n; i++) {
dp[i][i] = 1;
}
// 递推开始
// 先枚举子串长度
for(int L=2;L<=n;L++){
for(int i=0;i<n;i++){
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j=L+i-1;
if(j>=n){
break;
}
if(s[i]!=s[j]){
dp[i][j]=0;
}else{
if(j-i<3){
dp[i][j]=1;
}else{
dp[i][j]=dp[i+1][j-1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return 0;
}
42.接雨水
每个位置能够存储的水的量为两步:先找左边的墙的最高的高度h1,再找右边的墙的最高的高度h2;然后计算min(h1,h2)-height;
int trap(vector<int>& height)
{
int ans = 0;
int size = height.size();
//0位置和size-1位置都不行;
for (int i = 1; i < size - 1; i++) {
int max_left = 0, max_right = 0;
for (int j = i; j >= 0; j--) { //Search the left part for max bar size
max_left = max(max_left, height[j]);
}
for (int j = i; j < size; j++) { //Search the right part for max bar size
max_right = max(max_right, height[j]);
}
ans += min(max_left, max_right) - height[i];
}
return ans;
}
No.3 周赛
第 79 场双周赛
6083.判断─个数的数字计数是否等于数位的值
#include<bits/stdc++.h>
using namespace std;
#define N 10001
string s;
int check(int x){
int count=0;
for(auto i:s){
if(i-'0'==x)count++;
}
return count;
}
int main()
{
cin>>s;
int flag=0;
for(int i=0;i<s.size();i++){
int num=s[i]-'0';
if(num==check(i)){
flag=0;
}else{
flag=1;
}
if(flag==1)break;
}
if(flag==1)cout<<"flase"<<endl;
else cout<<"true"<<endl;
return 0;
}
6084.最多单词数的发件人
//在一串字符单词中比如:messages = ["Wonderful day Alice"],
//那么如何统计单词的个数,:即只需要统计空格的数量+1;
6085.道路的最大总重要性
思路及算法
计算每个城市的出现次数,排序后,次数多的分配当前最大的值
这里直接加到大根堆中弹出,效果与排序一致
10011.以组为单位订音乐会的门票
第 301 场周赛
6112.装满杯子需要的最短总时长
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 100100
int n,m,t,k,d;
int x,y,z;
char ch;
string s;
vector<int>v[N];
int main()
{
vector<int>count;
for(int i=0;i<3;i++){
cin>>x;
count.push_back(x);
}
int ans=0;
//进行贪心:每次装入需求最多的两杯:
while(count[0]!=0||count[1]!=0||count[2]!=0){
sort(count.begin(),count.end());
if(count[2]>0)count[2]--;
if(count[1]>0)count[1]--;
ans++;
}
cout<<ans<<endl;
return 0;
}
6113.无限集中的最小数字
第304场周赛:
使数组中所有元素都等于零
//对于N要进行适应性的更改,对于字段错误
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 100100
long long n,m,k,g,d,t;
int x,y,z;
char ch;
string str;
vector<int>v[N];
int main() {
vector<int>nums;
set<int>sc;
for(auto p:nums)sc.insert(p);
int res=0;
for(auto p:sc){
if(p>0)res++;
}
cout<<res<<endl;
return 0;
}
分组的最大数量
//对于N要进行适应性的更改,对于字段错误
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 100100
long long n,m,k,g,d,t;
int x,y,z;
char ch;
string str;
vector<int>v[N];
int main() {
sort(grades.begin(),grades.end());
int res=0;
int num=grades.size();
for(int i=1; ;i++){
if(num>=i){
res++;
num=num-i;
}else{
break;
}
}
return res;
return 0;
}
找到离给定两个节点最近的节点
最短路径问题:
「最短路径」算法有 最短路径-Dijkstra 和 无权值最短路径算法(BFS)
「Dijkstra」适用于加权有向图,没有负权重边,且无环,一般是求起点到其它所有点的最短路径,也可以改进为求两点的最短路径
「无权值最短路径算法(BFS)」适用于无权有向图,可以有环,一般是求两点的最短路径,也可以改进为求起点到其它所有点的最短路径
对于本题,每条边的权重均为 1,所以可以看作为无权值,我们使用上述的第二种方法「无权值最短路径算法(BFS)」
由于点与点之间最多只有一条边,所以可以简化「无权值最短路径算法(BFS)」
public int closestMeetingNode(int[] edges, int node1, int node2) {
int n = edges.length;
// 点 node1 和 node2 到其它所有点的最短路径
int[] dist1 = getDist(edges, node1);
int[] dist2 = getDist(edges, node2);
int ans = -1, min = (int) 1e5 + 7;
// 遍历可选择的「中间点」
for (int i = 0; i < n; i++) {
// 分别为 node1 和 node2 到 i 的最短路径
int d1 = dist1[i], d2 = dist2[i];
// 如果有一方到不了,则跳过
if (d1 == -1 || d2 == -1) continue;
int max = Math.max(d1, d2);
if (max < min) {
min = max;
ans = i;
}
}
return ans;
}
// 求点 s 到其它所有点的最短路径
private int[] getDist(int[] edges, int s) {
int d = 0;
int[] dist = new int[edges.length];
Arrays.fill(dist, -1);
while (s != -1) {
// 已经访问,考虑「环」的存在
if (dist[s] != -1) break;
dist[s] = d++;
// 下一个点
s = edges[s];
}
return dist;
}
图中的最长环
环问题:
图的最大环最长链 - Rogn - 博客园 (cnblogs.com)
这是一个模板问题、:
最长环问题:
最长链问题:
第306场周赛:
矩阵中的局部最大值
class Solution {
public:
vector<vector<int>> largestLocal(vector<vector<int>>& grid) {
vector<vector<int>>res;
vector<int>ans;
int n=grid.size();
int m=grid[0].size();
int len=n-3+1;
//以 多少个位置为左上角的起点构成3*3矩阵
// 每一行
for(int i=0;i<len;i++){
//每一列
ans.clear();
for(int j=0;j<len;j++){
int maxx=0;
for(int p=0;p<3;p++){
for(int q=0;q<3;q++){
maxx=max(maxx, grid[i+p][j+q]);
}
}
ans.push_back(maxx);
}
res.push_back(ans);
}
return res;
}
};
边积分最高的节点
class Solution {
public:
int edgeScore(vector<int>& edges) {
int n=edges.size();
long long sum[n];
memset(sum,0,sizeof(sum));
for(int i=0;i<n;i++){//加对应的值
sum[edges[i]]+=i;
}
long long max=0;
int loc=0;
for(int i=0;i<n;i++){//求出最大值
if(sum[i]>max){
max=sum[i];
loc=i;
}
}
return loc;
}
};
根据模式串构造最小数字
统计特殊整数:数位dp
第309场周赛:
6167. 检查相同字母间的距离
// 蛀牙是进行 字符字母 与数字的转化;和
ch='a';
char cc = ch+1;
cout<<cc<<endl; //结果为 b
class Solution {
public:
bool checkDistances(string s, vector<int>& distance) {
set<char>sc;
for(auto i:s)sc.insert(i);
for(auto i:sc){
int num=0;
for(int j=0;j<s.size();j++){
if(s[j]==i)num=j-num;
}
num--;
int wei = int(i)-97;
if(distance[wei]!=num){
return false;
}
}
return true;
}
};
6168. 恰好移动 k 步到达某一位置的方法数目
//对于N要进行适应性的更改,对于字段错误
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 100100
int n,m,k,g,d;
int x,y,z,t;
char ch;
string str;
vector<int>v[N];
long long mod = 1000000000+7;
long long sum = 0;
int endstop;
void dfs(int index,int citime,long long &sum){
if(citime<k){
dfs(index-1,citime+1,sum);
dfs(index+1,citime+1,sum);
}else if(citime==k){
if(index==endstop){
sum++;
sum=sum%mod;
}
return;
}else{
return;
}
}
int main() {
cin>>x>>endstop;
cin>>k;
dfs(x,0,sum);
cout<<sum<<endl;
return 0;
}
2401. 最长优雅子数组
滑动窗口:
res = max(res, right - left +1 ); 这个是关键;res记录过去(因为只是要结果。
class Solution {
public:
int longestNiceSubarray(vector<int>& nums) {
int n = nums.size();
if(n == 1)return 1;
int res = 1;
int left = 0;//记录当前优雅子数组
for(int right = 1;right < n;++i){
for(int i = right - 1;i >= left;--i){
if(nums[right] & nums[i]){
//说明right指向的位置和i不可以构成优雅子数组
left = i + 1;
break;
}
}
res = max(res,right - left + 1);
}
return res;
}
};
作者:sdy-r
链接:https://leetcode.cn/problems/longest-nice-subarray/solution/by-sdy-r-glyh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2402. 会议室 III
1.使用sort函数,重写排序规则。
struct node{
int x;
int y;
}a[10];
bool cmp(node a, node b)
{
return a.x > b.x; //从大到小排列
// return a.x < b.x; 从小到大排列
}
2.在结构体中自定义排序,重小于号
struct node{
int x;
int y;
bool operator<(const node &a) const{
return x < a.x; //从大到小排序
// x > a.x; //从小到大排序
}
//这里的排序是根据每个结构体内部的x进行排序
}a[10];
3、优先队列排序(priority_queue)的使用:
结构体排序 + 优先队列排序(priority_queue)
// 优先级队列问题。
第 310 场周赛:
子字符串的最优划分
很经典的题目
将区间分为最少组数
很经典
最长递增子序列 II
线段树知识。。
第312场周赛
2418. 按身高排序
class Solution {
public:
vector<string> sortPeople(vector<string>& names, vector<int>& heights) {
vector<int>pos;
vector<int>heightssss;
heightssss = heights;
sort(heights.begin(),heights.end());
int n =heights.size();
for(int i=n-1;i>=0;i--){
for(int j=0;j<n;j++){
if(heights[i]==heightssss[j]){
pos.push_back(j);
break;
}
}
}
vector<string>nameee;
for(int i=0;i<n;i++){
nameee.push_back(names[pos[i]]);
}
return nameee;
}
};
按位与最大的最长子数组
No.4 字符串
二进制数的间距:
#include <bits/stdc++.h>
using namespace std;
#define N 100100
#define inf 0x3f3f3f3f
int x,y,z;
int n,m,s,d,k;
string str;
char ch;
vector<int>v[N];
int main()
{
cin>>n;
vector<int>ans;
while(n!=0){
ans.push_back(n%2);
n=n/2;
}
for(int i=ans.size()-1;i>=0;i--){
cout<<ans[i];
}
return 0;
}
3. 无重复字符的最长子串:滑动窗口+哈希表
class Solution {
public:
int lengthOfLongestSubstring(string s) {
map<char,int>mp;
int n=s.size();
int res=0;
int right=0,left=0;
int flag=0;
for(;right<n;right++){
if(mp[s[right]]==1){
flag=1;
mp[s[right]]++;
}else{
mp[s[right]]++;
}
while(flag==1){
mp[s[left]]--;
if(mp[s[right]]==1)flag=0;
left++;
}
res=max(res,right-left+1);
}
return res;
}
};
20. 有效的括号:栈的对称
class Solution {
public:
bool isValid(string s) {
bool flag = true;
int n=s.size();
if(n%2!=0)return false;
stack<char>s1,s2;
for(int i=0;i<n;i++){
s1.push(s[i]);
}
while(s1.size()!=0){
char cc = s1.top();
s1.pop();
if(cc=='}'||cc==']'||cc==')'){
s2.push(cc);
}else{
if(s2.size()==0){
return false;
}
if(cc=='{'){
if(s2.top()=='}')s2.pop();
else return false;
}else if(cc=='('){
if(s2.top()==')')s2.pop();
else return false;
}else if(cc=='['){
if(s2.top()==']')s2.pop();
else return false;
}
}
}
if(s2.size()!=0)return false;
return true;
}
};
No.5 前缀和
【动画模拟】一文秒杀七道题 - 连续的子数组和 - 力扣(LeetCode)
前缀和+哈希表
要想着变化方程式,,比如哈希表的那个 pre[i] - pre[j-1] = k 变化为 pre[i] - k ; 找存在。
滑动窗口模板
滑动窗口是什么呢? 可能是 一个start指针,一个end指针,进而进行滑动,根据一些题目条件进行移动。。
《挑战程序设计竞赛》这本书中把滑动窗口叫做「虫取法」,我觉得非常生动形象。因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动。
我分享一个滑动窗口的模板,能解决大多数的滑动窗口问题:
def findSubArray(nums):
N = len(nums) # 数组/字符串长度
left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
sums = 0 # 用于统计 子数组/子区间 是否有效,根据题目可能会改成求和/计数
res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
sums += nums[right] # 增加当前右边指针的数字/字符的求和/计数
while 区间[left, right]不符合题意: # 此时需要一直移动左指针,直至找到一个符合题意的区间
sums -= nums[left] # 移动左指针前需要从counter中减少left位置字符的求和/计数
left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
# 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
res = max(res, right - left + 1) # 需要更新结果
right += 1 # 移动右指针,去探索新的区间
return res
滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。
模板的整体思想是:
-
定义两个指针
left
和right
分别指向区间的开头和结尾,注意是闭区间;定义sums
用来统计该区间内的各个字符出现次数; -
第一重
while
循环是为了判断right
指针的位置是否超出了数组边界;当right
每次到了新位置,需要增加right
指针的求和/计数; -
第二重 while 循环是让 left 指针向右移动到 [left, right] 区间符合题意的位置;当 left 每次移动到了新位置,需要减少 left 指针的求和/计数;
-
在第二重 while 循环之后,成功找到了一个符合题意的 [left, right] 区间,题目要求最大的区间长度,因此更新 res 为 max(res, 当前区间的长度) 。
-
right
指针每次向右移动一步,开始探索新的区间。 -
模板中的 sums 需要根据题目意思具体去修改,本题是求和题目因此把sums 定义成整数用于求和;如果是计数题目,就需要改成字典用于计数。当左右指针发生变化的时候,都需要更新 sums
209. 长度最小的子数组:滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int ans = INT_MAX;
int start = 0, end = 0;
int sum = 0;
while (end < n) {
sum += nums[end];
while (sum >= s) {
ans = min(ans, end - start + 1);
sum -= nums[start];
start++;
}
end++;
}
return ans == INT_MAX ? 0 : ans;
}
};
238. 除自身以外数组的乘积 前缀和+后缀和
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size();
int sumq[n];
int sumh[n];
for(int i=0;i<n;i++){
if(i==0)sumq[i]=nums[i];
else sumq[i]=sumq[i-1]*nums[i];
}
for(int i=n-1;i>=0;i--){
if(i==n-1)sumh[i]=nums[i];
else sumh[i]=sumh[i+1]*nums[i];
}
vector<int>ans;
ans.push_back(sumh[1]);
for(int i=1;i<n-1;i++){
ans.push_back(sumq[i-1]*sumh[i+1]);
}
ans.push_back(sumq[n-2]);
return ans;
}
};
1004. 最大连续1的个数 III:滑动窗口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGuEapDr-1679671888186)(D:/Typora/images/image-20221020153751247.png)]
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n=nums.size();
int left = 0,right=0,sum=0;
int res=0;
for( ;right<n;right++){
if(nums[right]==0)sum++;
while(sum>k){
if(nums[left]==0)sum--;
left++;
}
res=max(res,right-left+1);
}
return res;
}
};
1124. 表现良好的最长时间段:前缀和+单调栈
// 首先尝试滑动窗口 进行 求解,,但是不行,只能通过一半。
//我一开始也以为是滑动窗口,但想这样的case:[1,2,3,9,9,9,9,9];类似这样的滑窗做不了
class Solution {
public:
int longestWPI(vector<int>& hours) {
int n=hours.size();
int left=0,right=0,sum1=0;
int res=0;
for(;right<n;right++){
if(hours[right]>8)sum1++;
else sum1--;
while(sum1<=0&&left<=right){
if(sum1<=0&&left==right)break;
if(hours[left]>8)sum1--;
else sum1++;
left++;
}
res=max(res,right-left+1);
}
return res;
}
};
// 下面的有点偏暴力了
class Solution {
public:
int longestWPI(vector<int>& hours) {
int n=hours.size();
int sum[n];
int nums[n];
for(int i=0;i<n;i++){
if(hours[i]>8)nums[i]=1;
else nums[i]=-1;
}
sum[0]=nums[0];
for(int i=1;i<n;i++){
sum[i]=nums[i]+sum[i-1];
}
int left=0,right=0,sum1=0;
int res=0;
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
if(sum[j]-sum[i]+nums[i]>0){ //特别是这个判断条件的 +nums[i]
res=max(res,j-i+1);
}
}
}
return res;
}
};
724. 寻找数组的中心下标:前缀和
class Solution {
public:
int pivotIndex(vector<int>& nums) {
int n=nums.size();
int sum[n+1];
sum[0]=0;
for(int i=0;i<n;i++){
sum[i+1]=sum[i]+nums[i];
}
vector<int>ans;
for(int i=1;i<=n;i++){
int left = sum[i-1];
int right = sum[n]-sum[i];
if(left==right){
ans.push_back(i-1);
}
}
if(ans.size()==0)return -1;
else return ans[0];
}
};
560. 和为 K 的子数组:前缀和+哈希表
思路:其实我们现在这个题目和两数之和原理是一致的,只不过我们是将所有的前缀和该前缀和出现的次数存到了 map 里。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ikxzAXnr-1679671888186)(D:/Typora/images/image-20221025191243786.png)]
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
map<int,int>mp;
mp[0]=1;//是从开始到自身即pre[i]-k=0;即
int n=nums.size();
int pre[n+1];
pre[0]=0;
int res=0;
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]-k)!=mp.end()){
res+=mp[pre[i]-k];
}
mp[pre[i]]++;
}
return res;
}
};
1248. 统计「优美子数组」:前缀和+哈希表
思路”要想着变化方程式;;;
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int res=0;
int n=nums.size();
int pre[n+1];
pre[0]=0;
map<int,int>mp;
mp[0]=1;
for(int i=0;i<n;i++){
if(nums[i]%2==0){
nums[i]=0;
}else{
nums[i]=1;
}
}
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]-k)!=mp.end()){
res+=mp[pre[i]-k];
}
mp[pre[i]]++;
}
return res;
}
};
974. 和可被 K 整除的子数组:前缀和+哈希表
其中 巧妙的 还是 方程变化。 以及对 负数的处理。
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
map<int,int>mp;
mp[0]=1;
int n=nums.size();
int pre[n+1];
pre[0]=0;
int res=0;
for(int i=1;i<=n;i++){
// 注意负数 的问题。
pre[i]=pre[i-1]+nums[i-1];
if(mp.find((pre[i]%k+k)%k)!=mp.end()){
res+=mp[(pre[i]%k+k)%k];
}
mp[(pre[i]%k+k)%k]++;
}
return res;
}
};
523. 连续的子数组和:前缀和+哈希表
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int n = nums.size();
map<int,int>mp;
mp[0]=1;
int pre[n+1];
pre[0]=0;
int res = 0;
pre[1]=pre[0]+nums[0];
for(int i=2;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]%k)!=mp.end()){
res+=mp[pre[i]%k];
}
mp[pre[i-1]%k]++; // 因为个数限制。
}
if(res==0)return false;
else return true;
}
};
930. 和相同的二元子数组:前缀和+哈希表
class Solution {
public:
int numSubarraysWithSum(vector<int>& nums, int goal) {
map<int,int>mp;
mp[0]=1;
int n=nums.size();
int pre[n+1];
pre[0]=0;
int res=0;
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]-goal)!=mp.end()){
res+=mp[pre[i]-goal];
}
mp[pre[i]]++;
}
return res;
}
};
904. 水果成篮:滑动窗口+哈希表
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int res = 0;
int n =fruits.size();
int right=0,left=0;
set<int>sc;
map<int,int>mp;
for(;right<n;right++){
sc.insert(fruits[right]);
mp[fruits[right]]++;
while(sc.size()>2){
mp[fruits[left]]--;
if(mp[fruits[left]]==0)sc.erase(fruits[left]);
left++;
}
res=max(res,right-left+1);
}
return res;
}
};
1]=pre[0]+nums[0];
for(int i=2;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]%k)!=mp.end()){
res+=mp[pre[i]%k];
}
mp[pre[i-1]%k]++; // 因为个数限制。
}
if(res==0)return false;
else return true;
}
};
## [930. 和相同的二元子数组](https://leetcode.cn/problems/binary-subarrays-with-sum/):前缀和+哈希表
```c++
class Solution {
public:
int numSubarraysWithSum(vector<int>& nums, int goal) {
map<int,int>mp;
mp[0]=1;
int n=nums.size();
int pre[n+1];
pre[0]=0;
int res=0;
for(int i=1;i<=n;i++){
pre[i]=pre[i-1]+nums[i-1];
if(mp.find(pre[i]-goal)!=mp.end()){
res+=mp[pre[i]-goal];
}
mp[pre[i]]++;
}
return res;
}
};
904. 水果成篮:滑动窗口+哈希表
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int res = 0;
int n =fruits.size();
int right=0,left=0;
set<int>sc;
map<int,int>mp;
for(;right<n;right++){
sc.insert(fruits[right]);
mp[fruits[right]]++;
while(sc.size()>2){
mp[fruits[left]]--;
if(mp[fruits[left]]==0)sc.erase(fruits[left]);
left++;
}
res=max(res,right-left+1);
}
return res;
}
};