C++ 子数组子串

目录

最大子数组和

环形子数组的最大和

 乘积最大子数组

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

 等差数列划分

最长湍流子数组

单词拆分

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


最大子数组和

53. 最大子数组和

dp表示当前元素结束的最大连续子数组和

参考代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n);
        dp[0] = nums[0];
        int ret = dp[0];
        for(int i = 1; i < n; i++)
        {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]);
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

环形子数组的最大和

918. 环形子数组的最大和

细节:sum == g[i] 时返回f[0] 或sum 都可以,这时候全是负数f[i] 一定 >= g[i]

不相等情况下sum - g[i], 相等时为0(但其实这时候是最小值) ,所以要判断sum 与 g[i]是否相等

参考代码

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n), g(n);
        f[0] = g[0] = nums[0];
        int sum = 0;
        for(auto e : nums)
            sum += e;
        int ret = f[0];
        for(int i = 1; i < n; i++)
        {
            f[i] = max(f[i - 1] + nums[i], nums[i]);
            g[i] = min(g[i - 1] + nums[i], nums[i]);
            ret = max(ret, max(f[i], sum == g[i] ? f[0] : sum - g[i]));
        }
        
        return ret;
    }
};

 乘积最大子数组

152. 乘积最大子数组

乘法 : 负数 * 负数也是正数

f:表示到 i 位置结束的最大子串

g:表示到 i 位置结束的最小子串 

一共也就三种情况放一起比较,

错误:min和max写错

参考代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {//环形呢?
        int n = nums.size();
        vector<int> f(n), g(n);
        f[0] = g[0] = nums[0];
        int ret = f[0];
        for(int i = 1; i < n; i++)
        {
            f[i] = max(f[i - 1] * nums[i], max(g[i - 1] * nums[i], nums[i]));
            // g[i] = min(f[i - 1] * nums[i], max(g[i - 1] * nums[i], nums[i]));
            g[i] = min(f[i - 1] * nums[i], min(g[i - 1] * nums[i], nums[i]));
            ret = max(ret, f[i]);
        }
        return ret;
    }
};

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

1567. 乘积为正数的最长子数组长度

初始化:讨论第一个值正负来初始化f[0], g[0]

细节概念:在nums[i] > 0 的时候g(i) 不一定是g[i - 1] + 1,如果g[i - 1] == 0,那么这样操作里面就是一个正数;在nums[i] < 0 的时候,如果g[i - 1] == 0, 那么f[i]就成一个负数

错误:符号优先级g[i] = (g[i - 1] == 0 ? -1 : g[i - 1]) + 1;开始是想着-1 和 g[i - 1] , -1 是为了和掉+1,变成0,不如这么写:g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1;

参考代码

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n), g(n);
        if(nums[0] > 0) f[0] = 1;
        else if(nums[0] < 0) g[0] = 1;
        int ret = f[0];
        for(int i = 1; i < n; i++)
        {
            if(nums[i] > 0) 
            {
                f[i] = f[i - 1] + 1;
                g[i] = (g[i - 1] == 0 ? -1 : g[i - 1]) + 1;//符号优先级
            }
            else if(nums[i] < 0) 
            {
                f[i] = (g[i - 1] == 0 ? -1 : g[i - 1]) + 1;
                g[i] = f[i - 1] + 1;
            }
            ret = max(ret, f[i]);
        }
        return ret;
    }
};

 等差数列划分

 413. 等差数列划分

没有说是不同的等差数列

dp指的是已改位置为结尾的等差数列个数

dp[i] += dp[i - 1] + 1这里的 + 1 比如说i = 5, dp[4] 是转换过来是1 2 3 4 5; 2 3 4 5;这里的 + 1 是3 4 5;能延续的有dp[i - 1]个,新增一个

参考代码

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n);
        int ret = 0;
        for(int i = 2; i < n; i++)
            if(nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2])
                dp[i] += dp[i - 1] + 1, ret += dp[i]; //这个1表示的是哪个区间
        return ret;
    }
};

最长湍流子数组

978. 最长湍流子数组

 

初始化:示例3看出,只有一个元素时,湍流子数组长度为1,那就初始化为1;

dp表:f表示上升(当前元素 > 上一个元素) 时最大湍流子数组;g类似

参考代码

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        int n = arr.size();
        vector<int> f(n, 1), g(n, 1);
        int ret = 1;
        for(int i = 1; i < n; i++)
        {
            if(arr[i] > arr[i - 1]) f[i] = g[i - 1] + 1;
            else if(arr[i] < arr[i - 1]) g[i] = f[i - 1] + 1;
            ret = max(ret, max(f[i], g[i]));
        }
        return ret;
    }
};

单词拆分

139. 单词拆分

 

初始化:第一个字符

注意语法:count函数里面不能传char类型,所以不能传s[0],因为hash的模版参数是string

比方说 i 在这个位置是true 说明 [0, i] 这个区间可以由wordDict里面的字符串拼接成

j的扫描区间是[1, i] 从左从右都可以;

解释 j 不能从之前的true开始扫描,样例:

细节:因为 j 是从 1 开始的,那么就少了[0,  i]这个区间的判断,j 不从 0 开始是怕越界,且dp[j - 1]起到连接的操作(dp: 到i位置 (即区间[0, i])  能不能被拼接)

参考代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> hash;
        for(auto e : wordDict)
            hash.insert(e);
        int n = s.size();
        vector<bool> dp(n);
        // if(hash.count(s.substr(0, 1))) dp[0] = true;
        dp[0] = hash.count(s.substr(0, 1)) == 1 ? true : false;
        for(int i = 1; i < n; i++)
        {
            if(hash.count(s.substr(0, i + 1))) 
            {
                dp[i] = true; 
                continue;
            }
            for(int j = 1; j <= i; j++)
            {
                if(dp[j - 1] && hash.count(s.substr(j, i - j + 1)))
                {
                    dp[i] = true;
                    break;
                } 
            }
        }
        return dp[n - 1];
    }
};

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

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

dp[i] = dp[i - 1] + 1;这里的 + 1加的是自己,自己就是那个新的

dp[i] 就是当前元素的符合条件的个数

初始化:i从 1 开始dp[0] 为1, arr[s[0] - ' a '] ++;

参考代码1

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        int n = s.size();
        vector<int> dp(n, 1), arr(26);
        arr[s[0] - 'a']++;
        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] + 1;
            arr[s[i] - 'a'] = max(arr[s[i] - 'a'], dp[i]);
        }
        int ret = 0;
        for(auto e : arr)
        {
            ret += e;
        }
        return ret;
    }
};

也可以不减 'a' 开128 个就行

参考代码2

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        int n = s.size();
        vector<int> dp(n, 1), arr(128);
        arr[s[0]]++;
        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] + 1;
            arr[s[i]] = max(arr[s[i]], dp[i]);
        }
        int ret = 0;
        for(auto e : arr)
        {
            ret += e;
        }
        return ret;
    }
};

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值