当前位置:   article > 正文

子数组问题

子数组问题

目录

最大子数组和

环形子数组的最大和

乘积最大子数组

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

等差数列划分

最长湍流子数组

单词拆分

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


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

最大子数组和

题目

思路

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

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

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

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

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

填表顺序:从左往右。

返回值:dp表的最大值。

代码

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

题目

思路

本道题差别于上一道题的地方在于,数组是环形的,这样的话,在屡试不爽的采用“以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)。

代码

  1. class Solution {
  2. public:
  3. int maxSubarraySumCircular(vector<int>& nums) {
  4. int n=nums.size();
  5. vector<int> f(n+1);
  6. vector<int> g(n+1);
  7. int sum=0;
  8. for(int x:nums)
  9. sum+=x;
  10. for(int i=1;i<=n;i++){
  11. f[i]=max(nums[i-1],f[i-1]+nums[i-1]);
  12. g[i]=min(nums[i-1],g[i-1]+nums[i-1]);
  13. }
  14. int fmax=*max_element(f.begin()+1,f.end());
  15. int gmin=*min_element(g.begin()+1,g.end());
  16. if(gmin==sum) return fmax;
  17. else return max(fmax,sum-gmin);
  18. }
  19. };
乘积最大子数组

题目

思路

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

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

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

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

状态转移方程:

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

填表顺序:从左往右

返回值:f表最大值。

代码

  1. class Solution {
  2. public:
  3. int maxProduct(vector<int>& nums) {
  4. int n=nums.size();
  5. vector<int> f(n+1);
  6. vector<int> g(n+1);
  7. f[0]=g[0]=1;
  8. for(int i=1;i<=n;i++){
  9. f[i]=max(nums[i-1],max(f[i-1]*nums[i-1],g[i-1]*nums[i-1]));
  10. g[i]=min(nums[i-1],min(g[i-1]*nums[i-1],f[i-1]*nums[i-1]));
  11. }
  12. return *max_element(f.begin()+1,f.end());
  13. }
  14. };
乘数为正数的最长子数组长度

题目

思路

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

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

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

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

状态转移方程:

初始化:不用初始化

填表顺序:从左往右。

返回值:f表最大值。

代码

  1. class Solution {
  2. public:
  3. int getMaxLen(vector<int>& nums) {
  4. int n=nums.size();
  5. vector<int> f(n+1);
  6. vector<int> g(n+1);
  7. for(int i=1;i<=n;i++){
  8. if(nums[i-1]>0){
  9. f[i]=f[i-1]+1;
  10. g[i]=g[i-1]==0?0:g[i-1]+1;
  11. }
  12. else if(nums[i-1]<0){
  13. f[i]=g[i-1]==0?0:g[i-1]+1;
  14. g[i]=f[i-1]+1;
  15. }
  16. }
  17. return *max_element(f.begin()+1,f.end());
  18. }
  19. };
等差数列划分

题目

思路

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

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

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

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

初始化:不用初始化

填表顺序:从左往右。

返回值:dp表元素之和。

代码

  1. class Solution {
  2. public:
  3. int numberOfArithmeticSlices(vector<int>& nums) {
  4. int n=nums.size();
  5. if(n<3) return 0;
  6. vector<int> dp(n);
  7. for(int i=2;i<n;i++){
  8. if(nums[i-2]+nums[i]==2*nums[i-1])
  9. dp[i]=dp[i-1]+1;
  10. }
  11. int ret=0;
  12. for(int k:dp)
  13. ret+=k;
  14. return ret;
  15. }
  16. };
最长湍流子数组

题目

思路

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

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

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

状态转移方程:

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

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

代码

  1. class Solution {
  2. public:
  3. int maxTurbulenceSize(vector<int>& arr) {
  4. int n=arr.size();
  5. vector<int> f(n,1);
  6. vector<int> g(n,1);
  7. for(int i=1;i<n;i++){
  8. if(arr[i]>arr[i-1]) g[i]=f[i-1]+1;
  9. else if(arr[i]<arr[i-1]) f[i]=g[i-1]+1;
  10. }
  11. int a=*max_element(f.begin(),f.end());
  12. int b=*max_element(g.begin(),g.end());
  13. return max(a,b);
  14. }
  15. };
单词拆分

题目

思路

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

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

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

初始化:dp[0]=true

填表顺序:从左往右。

返回值:dp[n].

代码

  1. class Solution {
  2. public:
  3. bool wordBreak(string s, vector<string>& wordDict) {
  4. unordered_set<string> hash;
  5. for(string str:wordDict)
  6. hash.insert(str);
  7. int n=s.size();
  8. s=' '+s;
  9. vector<bool> dp(n+1);
  10. dp[0]=true;
  11. for(int i=1;i<=n;i++){
  12. for(int j=i;j>=1;j--){
  13. if(dp[j-1] && hash.count(s.substr(j,i-j+1))){
  14. dp[i]=true;
  15. break;
  16. }
  17. }
  18. }
  19. return dp[n];
  20. }
  21. };
环绕字符串中唯一的子字符串

题目

思路

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

状态表示: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中出现次数最多的那个即可,然后返回去重后的总和。

代码

  1. class Solution {
  2. public:
  3. int findSubstringInWraproundString(string s) {
  4. int n=s.size();
  5. vector<int> dp(n,1);
  6. for(int i=1;i<n;i++)
  7. if(s[i-1]+1==s[i] || (s[i-1]=='z' && s[i]=='a'))
  8. dp[i]+=dp[i-1];
  9. int arr[26];
  10. for(int i=0;i<n;i++){
  11. arr[s[i]-97]=max(arr[s[i]-97],dp[i]);
  12. }
  13. int ret=0;
  14. for(int i=0;i<26;i++)
  15. ret+=arr[i];
  16. return ret;
  17. }
  18. };

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/987551
推荐阅读
相关标签
  

闽ICP备14008679号