子数组问题

目录

最大子数组和

环形子数组的最大和

乘积最大子数组

乘数为正数的最长子数组长度

等差数列划分

最长湍流子数组

单词拆分

环绕字符串中唯一的子字符串


声明:接下来主要使用动态规划来解决问题!!!

最大子数组和

题目

思路

解决子数组问题,在接下来将屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:dp[i]表示以i位置为结尾的最大子数组和。

我们可以将上面很多情况分为两类:i单独为数组;i和前面的元素一起组成数组。

状态转移方程:dp[i]=max(nums[i],dp[i-1]+nums[i])

初始化:dp[0]=nums[0]。

填表顺序:从左往右。

返回值:dp表的最大值。

代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n=nums.size();
        vector<int> dp(n+1);
        for(int i=1;i<=n;i++)
            dp[i]=max(nums[i-1],dp[i-1]+nums[i-1]);
        return *max_element(dp.begin()+1,dp.end());    
    }
};
环形子数组的最大和

题目

思路

本道题差别于上一道题的地方在于,数组是环形的,这样的话,在屡试不爽的采用“以i位置为结尾”来分析问题时,还需要通过%模运算,有点麻烦。

通过分析不难发现,环形子数组的最大和只有两种情况:

其一是最大子数组在数组中间,其二是最大子数组在头和尾。

通过上图不难发现,第一种情况可以采用求“最大子数组”的方式,由于数组的总和是确定的,那么第二种情况可以采用求“最小子数组”的方式。

状态表示:f[i]表示以i位置为结尾的最大子数组的和;

                  g[i]表示以i位置为结尾的最小子数组的和。

状态转移方程:f[i]=max(nums[i],f[i-1]+nums[i])

                         g[i]=min(nums[i],g[i-1]+nums[i])

初始化:不用初始化。

填表顺序:从左往右。

返回值:如果sum=gmin,返回fmax;否则,返回max(fmax,sum-gmin)。

代码

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n=nums.size();
        vector<int> f(n+1);
        vector<int> g(n+1);
        int sum=0;
        for(int x:nums)
            sum+=x;
        for(int i=1;i<=n;i++){
            f[i]=max(nums[i-1],f[i-1]+nums[i-1]);
            g[i]=min(nums[i-1],g[i-1]+nums[i-1]);
        }
        int fmax=*max_element(f.begin()+1,f.end());
        int gmin=*min_element(g.begin()+1,g.end());
        if(gmin==sum) return fmax;
        else return max(fmax,sum-gmin);
    }
};
乘积最大子数组

题目

思路

解决子数组问题,依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:f[i]表示以i位置为结尾的最大子数组和;

                  g[i]表示以i位置为结尾的最小子数组和。

我们可以将上面很多情况分为两类:i单独为数组;i和前面的元素一起组成数组(又分为nums[i]>0  和 nums[i]<0)。

状态转移方程:

初始化:f[0]=g[0]=0

填表顺序:从左往右

返回值:f表最大值。

代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n=nums.size();
        vector<int> f(n+1);
        vector<int> g(n+1);
        f[0]=g[0]=1;
        for(int i=1;i<=n;i++){
            f[i]=max(nums[i-1],max(f[i-1]*nums[i-1],g[i-1]*nums[i-1]));
            g[i]=min(nums[i-1],min(g[i-1]*nums[i-1],f[i-1]*nums[i-1]));
        }
        return *max_element(f.begin()+1,f.end());
    }
};
乘数为正数的最长子数组长度

题目

思路

解决子数组问题,依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:f[i]表示以i位置为结尾乘积为正数的最长子数组的长度;

                  g[i]表示以i位置为结尾乘积为负数的最长子数组的长度。

我们可以将上面很多情况分为两类:i单独为数组(又分为nums[i]>0  和 nums[i]<0);i和前面的元素一起组成数组(又分为nums[i]>0  和 nums[i]<0)。

状态转移方程:

初始化:不用初始化

填表顺序:从左往右。

返回值:f表最大值。

代码

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n=nums.size();
        vector<int> f(n+1);
        vector<int> g(n+1);
        for(int i=1;i<=n;i++){
            if(nums[i-1]>0){
                f[i]=f[i-1]+1;
                g[i]=g[i-1]==0?0:g[i-1]+1;
            }
            else if(nums[i-1]<0){
                f[i]=g[i-1]==0?0:g[i-1]+1;
                g[i]=f[i-1]+1;
            }
        }
        return *max_element(f.begin()+1,f.end());
    }
};
等差数列划分

题目

思路

解决子数组问题,依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:dp[i]表示以i位置元素为结尾的等差数列的个数。

状态转移方程:if(nums[i-2]+nums[i]==2*nums[i-1])

                                  dp[i]=dp[i-1]+1;

初始化:不用初始化

填表顺序:从左往右。

返回值:dp表元素之和。

代码

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        int n=nums.size();
        if(n<3) return 0;
        vector<int> dp(n);
        for(int i=2;i<n;i++){
            if(nums[i-2]+nums[i]==2*nums[i-1])
                dp[i]=dp[i-1]+1;
        }
        int ret=0;
        for(int k:dp)
            ret+=k;
        return ret;
    }
};
最长湍流子数组

题目

思路

解决子数组问题,依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:f[i]表示以i位置元素为结尾,呈“上升”趋势的湍流子数组的长度;

                  g[i]表示以i位置元素为结尾,呈“下降”趋势的湍流子数组的长度。

状态转移方程:

初始化:全都初始化为1.

返回值:两个表的最大值。

代码

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        int n=arr.size();
        vector<int> f(n,1);
        vector<int> g(n,1);
        for(int i=1;i<n;i++){
            if(arr[i]>arr[i-1]) g[i]=f[i-1]+1;
            else if(arr[i]<arr[i-1]) f[i]=g[i-1]+1; 
        }
        int a=*max_element(f.begin(),f.end());
        int b=*max_element(g.begin(),g.end());
        return max(a,b);
    }
};
单词拆分

题目

思路

解决子数组问题,依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:dp[i]表示s字符串[0,i]区间内的子字符串可以拆分出在字典中出现的单词。

状态转移方程:dp[i]=dp[j-1]==true && s的[j,i]区间字符串有在字典中出现。【0<=j<i】

初始化:dp[0]=true

填表顺序:从左往右。

返回值:dp[n].

代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> hash;
        for(string str:wordDict)
            hash.insert(str);
        int n=s.size();
        s=' '+s;
        vector<bool> dp(n+1);
        dp[0]=true;
        for(int i=1;i<=n;i++){
            for(int j=i;j>=1;j--){
                if(dp[j-1] && hash.count(s.substr(j,i-j+1))){
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[n];
    }
};
环绕字符串中唯一的子字符串

题目

思路

我们依旧屡试不爽的采用“以某个位置为结尾”来分析问题。

状态表示:dp[i]表示以i位置元素为结尾的子字符串在base中出现的次数。

状态转移方程:

            if(s[i-1]+1==s[i] || (s[i-1]=='z' && s[i]=='a'))

                dp[i]+=dp[i-1];

初始化:全都初始化为1.

返回值:由于题目中要求统计并返回 s 中有多少 不同非空子串 也在 base 中出现,因此我们需要做“去重”操作,对于两个相同字符,只需统计以该字符结尾在base中出现次数最多的那个即可,然后返回去重后的总和。

代码

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        int n=s.size();
        vector<int> dp(n,1);
        for(int i=1;i<n;i++)
            if(s[i-1]+1==s[i] || (s[i-1]=='z' && s[i]=='a'))
                dp[i]+=dp[i-1];
        int arr[26];
        for(int i=0;i<n;i++){
            arr[s[i]-97]=max(arr[s[i]-97],dp[i]);
        }
        int ret=0;
        for(int i=0;i<26;i++)
            ret+=arr[i];
        return ret;
    }
};

  • 35
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 34
    评论
最大数组问题是一个经典的算法问题,可以使用C语言来实现。 首先,我们可以定义一个函数来计算最大数组的和,传入一个整型数组数组的大小。如下所示: ```c int maxSubarraySum(int arr[], int size); ``` 然后,在函数内部,我们可以使用动态规划的思想来解决这个问题。我们可以创建一个变量`maxSum`和`currentSum`,分别表示当前最大数组的和和当前累加的和。初始化`maxSum`为数组第一个元素,`currentSum`为0。 接下来,我们使用一个循环遍历整个数组。在循环内部,我们首先将`currentSum`加上当前元素的值,然后判断`currentSum`是否大于`maxSum`,如果是,则将`maxSum`更新为`currentSum`。如果`currentSum`小于0,则将`currentSum`重置为0,相当于从当前位置重新开始累加。 最后,循环结束后,我们可以得到最大的数组和`maxSum`。返回`maxSum`即可。 下面是完整的代码实现: ```c #include <stdio.h> int maxSubarraySum(int arr[], int size) { int maxSum = arr[0]; int currentSum = 0; for (int i = 0; i < size; i++) { currentSum += arr[i]; if (currentSum > maxSum) { maxSum = currentSum; } if (currentSum < 0) { currentSum = 0; } } return maxSum; } int main() { int arr[] = { -2, 1, -3, 4, -1, 2, 1, -5, 4 }; int size = sizeof(arr) / sizeof(arr[0]); printf("最大数组的和为:%d\n", maxSubarraySum(arr, size)); return 0; } ``` 运行这段代码,输出结果为:最大数组的和为:6。这个结果对应的最大数组为:[4, -1, 2, 1]。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新绿MEHO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值