传送门:https://leetcode-cn.com/probl...
首先预估一下暴力解:循环遍历每一个子数组,判断是否为优美子数组,判断过程需要求得该数组的奇数个数。预估一下O(n3)不可取。
动态规划
对于这种可以将问题分解,并利用各阶段结果逐个求解。DP当然是合适不过:
1.首先求解以每个数为起点的子数组,将问题拆分为N次dp。
dp[i]含义为 以 i 点之前奇数的个数。在每一次dp计算过程中,求得第一次出现优美数组,即dp[i] == tar,到出现下一个奇数的数字个数,即 dp[i] > tar。这个值便是这次dp的解。
即:假设k=2
1 | 1 | 2 | 1 | 2 | 1 | 2 | 2 |
---|---|---|---|---|---|---|---|
以该数为起点 | 第一次出现优美子数组 | 出现下一个奇数 |
int numberOfSubarrays(vector<int>& nums, int tar) {
int length = nums.size();
int result = 0;
int dp[length];
for(int i=0;i<length-tar+1;++i){
memset(&dp,0,sizeof(int)*length);
if(nums[i]%2!=0) dp[i] = 1;
if(dp[i]==1&&tar==1)result++;
for(int j=i+1;j<length;++j){
dp[j] = nums[j]%2 == 0 ? dp[j-1] : dp[j-1] + 1;
result = dp[j] == tar ? result+1 : result;
if(dp[j]>tar) break;
}
}
return result;
}
2.换一种DP思路,虽然时间复杂度没有改变,但也是换一种思路。两者差别为 ① 中固定了起点,计算这个起点有多少个优美子数组;② 中有一个“不太一样”的终点,因为它并没有固定,只是计算在它之前有多少优美子数组
新引入一个have数组(为了和dp对上,简写为he),he[i] 含义为 i之前有多少奇数。
dp数组的含义与上一条不太一样,dp[i] 含义为 i 数之前有多少优美子数组。
则dp[i] = dp[i-1] + Σ ( 固定 i 点为终点的子数组中 优美子数组的个数),这里需要对 i (以i为终点恰好也是i个子数组) 个子数组循环求解。由于引入he数组,求 i 个子数组只需要一次遍历。
int numberOfSubarrays(vector<int>& nums, int tar) {
int length = nums.size();
if(length==0||length<tar) return 0;
int result = 0;
int dp[length];
for(int i=0;i<length-tar+1;++i){
memset(&dp,0,sizeof(int)*length);
if(nums[i]%2!=0) dp[i] = 1;
if(dp[i]==1&&tar==1)result++;
for(int j=i+1;j<length;++j){
dp[j] = nums[j]%2 == 0 ? dp[j-1] : dp[j-1] + 1;
result = dp[j] == tar ? result+1 : result;
if(dp[j]>tar) break;
}
}
return result;
}
小有提升,是不是用例的问题?
滑动窗口
看到题解中滑动窗口、哈希,受到了启发。
可以将解答的过程分为几个步骤:
1.一次遍历,求出每个奇数的位置,用哈希存储。可以得到多个“最小优美子数组”。
2.对于每个最小优美子数组,就像一个个窗口。向左滑动,可以有left个选择,向右滑动,可以有right个选择。
3.由于第一步中求出了每个奇数的位置,现在可以直接访问取到left、right的值,代价为O(1)。这一次的结果result = left * right
//i:起点,j:终点
//满足:j-i == tar - 1
left = myMap[i] - myMap[i-1]
right = myMap[j+1] - myMap[j]
//考虑边界条件:
left = i-1 < 0 ? myMap[0]+1 : myMap[i] - myMap[i-1] ;
right = j+1==mapLoc ? length-myMap[mapLoc-1] : myMap[j+1] - myMap[j];
完整代码如下:
int numberOfSubarrays(vector<int>& nums, int tar) {
int length = nums.size();
int he[length];memset(&he,0,sizeof(int)*length);
he[0] = nums[0]%2;
map<int,int> myMap;
int nowLoc = 0;int mapLoc = 0;
if(he[0]){
myMap[mapLoc] = 0;mapLoc++;
if(tar==1) nowLoc = 0;
}
for(int i=1;i<length;++i){
he[i] = he[i-1] + nums[i]%2;
if(he[nowLoc]!=he[i]){
myMap[mapLoc] = i;
mapLoc++;
nowLoc = i;
}
}
int result = 0;
for(int i=0,j=tar-1;j<mapLoc;++i,++j){
int left = i-1 < 0 ? myMap[0]+1 : myMap[i] - myMap[i-1] ;
int right = j+1==mapLoc ? length-myMap[mapLoc-1] : myMap[j+1] - myMap[j];
result += right*left;
}
return result;
}
还能提高吗?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。