传送门: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;
    }


还能提高吗?


HHXXHGGZ
0 声望0 粉丝