算法总结:连续子串

来源:互联网 发布:软件设计师视频教程 编辑:程序博客网 时间:2024/06/14 22:30

求子串满足某一条件的问题一般可以转化为先求以第i(0 < i <=n)个元素结尾满足该条件的解,然后将所有解加起来即可得到原问题的解。正是因为我们将以每个元素作为结尾来定义解,所以可以包含所有解,并且不会重复

例子

求一个数组中连续子数组中任何数字出现次数不大于k的所有子数组
1 2 2 4
1
答案是6
1、2、2、4、1 2、2 4

要求出所有满足条件的子数组,只要求出以nums[i]结尾的满足条件的所有数组长度的和。用上面的例子说明一下:

当 i = 0 时以1结尾的满足条件的数组长度是1,满足条件的子数组是[1]
当 i = 1 时以2结尾的满足条件的数组长度是2 即[1, 2],满足条件的子数组是[2]、[1, 2]
当 i = 2 时以2结尾的满足条件的数组长度是1 即[2],满足条件的子数组是[2](这个2和i=1时的2不是一个2,没有重复)
当 i = 3 时以4结尾的满足条件的数组长度是2 即[2,4],满足条件的子数组是[4]、[2, 4]

实现代码

#include <iostream>#include <unordered_map>#include <vector>#include <algorithm>using namespace std;int main(int argc, char const *argv[]){    int k;    int count;    cin >> count;  //输入元素个数    cin >> k;    if(k == 0){        cout << 0 << endl;        return 0 ;    }    vector<int> nums;    for(int i = 0; i < count; i++)    {        int elem;        cin >> elem;        nums.push_back(elem);    }    unordered_map<int, int> hash;    std::vector<int> dp(count, 0);    //i 和 j分别代表子数组的头部和尾部    for(int i = 0,j = 0; j < count; j++)    {        ++hash[nums[j]];        //如果满足条件子数组长度加1        if(hash[nums[j]] <= k)        {            dp[j] = j - i + 1;        }        如果不满足条件,说明nums[j]出现的次数大于了k。此时移动i直到去除一个nums[j]为止,这样子数组中nums[j]的个数就为k,满足条件。        else        {            --hash.[nums[i++]];            while(nums[i-1] != nums[j])            {                   --hash[nums[i++]];            }            dp[j] = j - i + 1;        }    }    int sum = accumulate(dp.begin(), dp.end(), 0);    cout << sum << endl;    return 0;}

Arithmetic Slices
已知一个数组,求这个数组中可以切割出多少个长度大于3的连续等差数列
A = [1, 2, 3, 4]
return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

和第一个题一样,求出每个以nums[i]结尾的解即可

因为子数组长度要大于3所以从i=2开始 res[0] = 0, dp[1] = 0;
当i = 2 时[1, 2, 3] 满足条件所以dp[2] = 1
当i = 3 时[2, 3, 4]、[1, 2, 3, 4]满足条件所以dp[2] = 2
答案是0 + 0 +1 + 2 = 3;

注意到dp[i]和dp[i-1]的关系

以[1, 2, 3, 4, 5]为例,以4结尾的大于等于3的等差数列加上一个5肯定是满足要求的,[3, 4]不满足要求,但[3, 4, 5]满足,所以如果nums[i] - nums[i-1] == nums[i-1]-nums[i - 2] 那么dp[i] = dp[i-1]+1;否则dp[i]=0;因此没有必要每次去找满足条件的最长子数组,用这个状态转移方程就可以了。最后把结果加起来就可以了。

代码如下

class Solution {public:    int numberOfArithmeticSlices(vector<int>& A) {        int n = A.size();        if (n < 3) return 0;        vector<int> dp(n, 0);         if (A[2]-A[1] == A[1]-A[0]) dp[2] = 1;         int result = dp[2];        for (int i = 3; i < n; ++i) {            if (A[i]-A[i-1] == A[i-1]-A[i-2])                 dp[i] = dp[i-1] + 1;            result += dp[i];        }        return result;    }};
原创粉丝点击