713. 乘积小于 K 的子数组
给你一个整数数组
nums
和一个整数k
,请你返回子数组内所有元素的乘积严格小于k
的连续子数组的数目。输入:nums = [10,5,2,6], k = 100 输出:8 解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。 需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
滑动窗口
int numSubarrayProductLessThanK(int* nums, int numsSize, int k) {
int ans=0;
int l=0;
int sum=1;
for(int r=0;r<numsSize;++r){
sum*=nums[r];
while(l<=r&&sum>=k){//需要防止k为0的情况,sum会一直大于k,l会越界
sum/=nums[l];
l++;
}
ans+=r-l+1;
}
return ans;
}
643. 子数组最大平均数 I
给你一个由
n
个元素组成的整数数组nums
和一个整数k
。请你找出平均数最大且 长度为
k
的连续子数组,并输出该最大平均数。任何误差小于
10-5
的答案都将被视为正确答案。输入:nums = [1,12,-5,-6,50,3], k = 4 输出:12.75 解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
double findMaxAverage(int* nums, int numsSize, int k) {
int ans=INT_MIN;
int l=0;
int sum=0;
for(int r=0;r<numsSize;++r){
sum+=nums[r];
if(r-l+1>k){
sum-=nums[l];
l++;
}
//满足一个特定条件才会更新
if(r-l+1==k){
ans=fmax(ans,sum);
}
}
return (double)ans/k;
}
3. 无重复字符的最长子串
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
滑动窗口+哈希表
int lengthOfLongestSubstring(char* s) {
int ans=0;
int cnt[128]={0};
int l=0,r=0,cur=0;
int n=strlen(s);
while(r<n){
//第一次遇到的字母,r后移
if(cnt[s[r]]==0){
cnt[s[r]]++;
cur++;
r++;
}else{
//区间内有重复字母,此时r不会动,只有l动,直到消除了重复字符
cnt[s[l]]--;
cur--;
l++;
}
ans=fmax(ans,cur);
}
return ans;
}
209. 长度最小的子数组
给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其总和大于等于
target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
int minSubArrayLen(int target, int* nums, int numsSize) {
int ans=INT_MAX;
int l=0;
int sum=0;
for(int r=0;r<numsSize;++r){
sum+=nums[r];
while(sum>=target){
ans=fmin(ans,r-l+1);//求最短长度,因此一旦满足条件,马上记录
sum-=nums[l];
l++;
}
}
return ans==INT_MAX?0:ans;
}
567. 字符串的排列
给你两个字符串
s1
和s2
,写一个函数来判断s2
是否包含s1
的排列。如果是,返回true
;否则,返回false
。换句话说,s1
的排列之一是s2
的 子串 。输入:s1 = "ab" s2 = "eidbaooo" 输出:true 解释:s2 包含 s1 的排列之一 ("ba").
查找s2中是否有s1的排列,维护一个大小为n的滑动窗口 ,用哈希表记录窗口内的字母,直到发现与s1完全相同的字母排列。
bool check(int* cnt1,int* cnt2){
for(int i=0;i<26;++i){
if(cnt1[i]!=cnt2[i]){
return false;
}
}
return true;
}
bool checkInclusion(char* s1, char* s2) {
int n=strlen(s1),m=strlen(s2);
if(n>m) return false;
int cnt1[26]={0},cnt2[26]={0};
for(int i=0;i<n;++i){
cnt1[s1[i]-'a']++;
cnt2[s2[i]-'a']++;
}
if(check(cnt1,cnt2)) return true;
for(int i=n;i<m;++i){
//滑动窗口
cnt2[s2[i]-'a']++;
cnt2[s2[i-n]-'a']--;
if(check(cnt1,cnt2)){
return true;
}
}
return false;
}
594. 最长和谐子序列
和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是
1
。现在,给你一个整数数组
nums
,请你在所有可能的子序列中找到最长的和谐子序列的长度。数组的子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变其余元素的顺序而得到。
输入:nums = [1,3,2,2,5,2,3,7] 输出:5 解释:最长的和谐子序列是 [3,2,2,2,3]
虽然求的是子序列的长度,但是需要比较的只有一个序列中的最大值与最小值,且差正好是1,故可以先进行排序,在顺序序列中区找。
int cmp(int* a,int* b){
return *a-*b;
}
int findLHS(int* nums, int numsSize){
qsort(nums,numsSize,sizeof(int),cmp);
int ans=0;
int l=0;
for(int r=0;r<numsSize;++r){
while(nums[r]-nums[l]>1){
l++;
}
//满足条件才会更新ans,因为存在相减等于0的情况不应该更新
if(nums[r]-nums[l]==1){
ans=fmax(ans,r-l+1);
}
}
return ans;
}
1512. 好数对的数目
给你一个整数数组
nums
。如果一组数字
(i,j)
满足nums[i]
==nums[j]
且i
<j
,就可以认为这是一组 好数对 。返回好数对的数目。输入:nums = [1,2,3,1,1,3] 输出:4 解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始
对于每一个元素来说,若在它出现之前相同元素已经出现n次,则以该元素作为(i,j)中j的好数对个数为n,想要统计好数对的个数,就是要统计每一个元素前它已经出现的次数
int numIdenticalPairs(int* nums, int numsSize) {
int hash[101]={0};
int ans=0;
for(int i=0;i<numsSize;++i){
ans+=hash[nums[i]];
hash[nums[i]]++;
}
return ans;
}
2006. 差的绝对值为 K 的数对数目
给你一个整数数组
nums
和一个整数k
,请你返回数对(i, j)
的数目,满足i <=j
且|nums[i] - nums[j]| == k
。|x|
的值定义为:如果
x >= 0
,那么值为x
。如果
x < 0
,那么值为-x
。输入:nums = [1,2,2,1], k = 1 输出:4 解释:差的绝对值为 1 的数对为: - [1,2,2,1] - [1,2,2,1] - [1,2,2,1] - [1,2,2,1]
要找满足i < j 且|nums[i] - nums[j]| == k的数对即对于j来说,找在j前面满足nums[i] = nums[j] - k 或nums[i] = nums[j] - k 的i的个数
int countKDifference(int* nums, int numsSize, int k) {
int ans=0;
int hash[101]={0};
for(int i=0;i<numsSize;++i){
int x1=nums[i]-k;
if(x1>=0&&x1<101){
ans+=hash[x1];
}
int x2=nums[i]+k;
if(x2>=0&&x2<101){
ans+=hash[x2];
}
hash[nums[i]]++;
}
return ans;
}
930. 和相同的二元子数组
给你一个二元数组
nums
,和一个整数goal
,请你统计并返回有多少个和为goal
的 非空 子数组。子数组 是数组的一段连续部分。输入:nums = [1,0,1,0,1], goal = 2 输出:4 解释:有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]
首先将该数组进行加工,使得第i项为前i项的和,由于该题中要使子数组的和为goal,即要让nums[i]到nums[j]的和为goal,就是要让加工后的数组nums [ j ] - nums [ i-1 ] == goal,用i代换i-1,得nums [ j ] - nums [ i ] == goal,则nums [ j ] == nums [ i ] + goal
int numSubarraysWithSum(int* nums, int numsSize, int goal){
//记录前缀和
for(int i=1;i<numsSize;++i){
nums[i]+=nums[i-1];//计算前缀和
}
int hash[30001]={0};
int sum=0;
//nums[i]-nums[j]==goal,则nums[j]==nums[i]-goal
for(int i=0;i<numsSize;++i){
int x=nums[i]-goal;
if(x>=0){
sum+=hash[x];
}
hash[nums[i]]++;
}
//最后加上前缀和为goal的个数
sum+=hash[goal];
return sum;
}
1004. 最大连续1的个数 III
给定一个二进制数组
nums
和一个整数k
,如果可以翻转最多k
个0
,则返回 数组中连续1
的最大个数 。输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2 输出:6 解释:[1,1,1,0,0,1,1,1,1,1,1]粗体数字从 0 翻转到 1,最长的子数组长度为 6。
int longestOnes(int* nums, int numsSize, int k) {
int ans=0;
int l=0;
int zero=0;
for(int r=0;r<numsSize;++r){
if(nums[r]==0){
zero++;
}
while(zero>k){
if(nums[l]==0) zero--;
l++;
}
ans=fmax(ans,r-l+1);
}
return ans;
}
前缀和+模拟
注意前缀和是从下标1开始存放的,presum[0]没有实际意义
枚举两个子数组的起始坐标:第一个子数组的和为presum[i+firstLen]-presum[i]、第二个子数组的和为presum[j+secondLen]-presum[j];
需要注意j需要分成两段进行遍历
int maxSumTwoNoOverlap(int* nums, int numsSize, int firstLen, int secondLen) {
int presum[numsSize+1];
int mmax=0;
presum[0]=0;
for(int i=1;i<=numsSize;++i){
presum[i]=presum[i-1]+nums[i-1];
}
int i=0,j=0;//i标志firstlen长度的子数组起始位,j标志secondlen子数组的起始位
for(i=0;i<=numsSize-firstLen;++i){
//j在i前面
for(j=0;j<=i-secondLen;++j){
int cur=presum[i+firstLen]-presum[i]+presum[j+secondLen]-presum[j];
if(mmax<cur){
mmax=cur;
}
}
//j在i后面
for(j=i+firstLen;j<=numsSize-secondLen;++j){
int cur=presum[i+firstLen]-presum[i]+presum[j+secondLen]-presum[j];
if(mmax<cur){
mmax=cur;
}
}
}
return mmax;
}
1156. 单字符重复子串的最大长度
如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。
给你一个字符串
text
,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。输入:text = "ababa" 输出:3
int maxRepOpt1(char* text) {
int n=strlen(text);
int l=0,r=0;
int ans=0;
int flag[26]={0};
for(int i=0;i<n;++i) flag[text[i]-'a']++;
while(r<n){
while(r<n&&text[r]==text[l]){
r++;
}
int nextleft=r;
r++;//跳过一个不同的字母,继续查找
while(r<n&&text[r]==text[l]){
//只相隔一个字母,就继续累加
r++;
}
int len=fmin(r-l,flag[text[l]-'a']);//最长不能超过同类字母的最大值
ans=fmax(ans,len);
r=nextleft;
l=nextleft;
}
return ans;
}
1759. 统计同质子字符串的数目
给你一个字符串
s
,返回s
中 同质子字符串 的数目。由于答案可能很大,只需返回对109 + 7
取余 后的结果。同质字符串 的定义为:如果一个字符串中的所有字符都相同,那么该字符串就是同质字符串。
子字符串 是字符串中的一个连续字符序列。
输入:s = "abbcccaa" 输出:13 解释:同质子字符串如下所列: "a" 出现 3 次。 "aa" 出现 1 次。 "b" 出现 2 次。 "bb" 出现 1 次。 "c" 出现 3 次。 "cc" 出现 2 次。 "ccc" 出现 1 次。 3 + 1 + 2 + 1 + 3 + 2 + 1 = 13
统计同质子序列的长度,每次加上即可 如a、aa、aaa,为3+2+1=6
int countHomogenous(char * s){
int n=strlen(s);
int ans=1;
int tmp=1;
//统计同质子序列的长度,每次加上即可
for(int i=1;i<n;++i){
if(s[i]==s[i-1]){
tmp++;
}else{
tmp=1;
}
ans=(ans+tmp)%100000007;
}
return ans;
}
标准滑动窗口解法:
int countHomogenous(char * s){
int n=strlen(s);
int ans=0;
int l=0,r=0;
while(r<n){
if(s[l]!=s[r]){
//如果当前不是重复的字符
l=r;
}else{
//如果当前是重复字符
ans=(ans+r-l+1)%1000000007;
r++;
}
}
return ans;
}
1839. 所有元音按顺序排布的最长子字符串
当一个字符串满足如下条件时,我们称它是 美丽的 :
- 所有 5 个英文元音字母(
'a'
,'e'
,'i'
,'o'
,'u'
)都必须 至少 出现一次。- 这些元音字母的顺序都必须按照 字典序 升序排布(也就是说所有的
'a'
都在'e'
前面,所有的'e'
都在'i'
前面,以此类推)比方说,字符串
"aeiou"
和"aaaaaaeiiiioou"
都是 美丽的 ,但是"uaeio"
,"aeoiu"
和"aaaeeeooo"
不是美丽的 。给你一个只包含英文元音字母的字符串
word
,请你返回word
中 最长美丽子字符串的长度 。如果不存在这样的子字符串,请返回0
。输入:word = "aeiaaioaaaaeiiiiouuuooaauuaeiu" 输出:13 解释:最长子字符串是 "aaaaeiiiiouuu" ,长度为 13 。
用type用于记录窗口中的字母的类别个数
int longestBeautifulSubstring(char* word) {
int n=strlen(word);
if(n<5) return 0;
int ans=0,type=1;
int l=0;
for(int r=1;r<n;++r){
//字典序为逆序,重新计算窗口
if(word[r-1]>word[r]){
l=r;
type=1;
}
//字典序顺序,但是遇到不同字母,要将种类加1
if(word[r-1]<word[r]){
type++;
}
//若遇到的是相同的字母,什么也不做,直接让r后移
//如果字母的种类达到5,就更新答案
if(type==5){
ans=fmax(ans,r-l+1);
}
}
return ans;
}
763. 划分字母区间
给你一个字符串
s
。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是
s
。返回一个表示每个字符串片段的长度的列表。
输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释:划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
int* partitionLabels(char* s, int* returnSize) {
int cnt[26]={0};
int n=strlen(s);
//记录每个字符最远的下标
for(int i=0;i<n;++i){
cnt[s[i]-'a']=i;
}
int *ans=malloc(sizeof(int)*n);
int l=0,r=0;
int pos=0;
for(int i=0;i<n;++i){
//r为每个分组的最远的下标
r=fmax(r,cnt[s[i]-'a']);
if(i==r){
ans[pos++]=r-l+1;
l=r+1;
}
}
*returnSize=pos;
return ans;
}