【算法】【Dynamic Programming】Wiggle Subsequence

Difficulty:Medium

Description

A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Examples:
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].

Input: [1,2,3,4,5,6,7,8,9]
Output: 2

Follow up:
Can you do it in O(n) time?

Solution

思路

max[0][i]:从0到i的最大wiggle subsequence长度,首个difference为negative.

max[1][i]:从0到i的最大wiggle subsequence长度,首个difference为positive.

max[j][k]={max[j][k1]+1,max[j][k1],differencedifference

其中j = 0, 10 ≤ k ≤ nums.length()max[0][0] = max[1][0] = 1

为区分前后两个difference符号是否相同,还要创建bool类型数组m[2], 分别记录max[0]max[1]上一个difference是positive还是negative.

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int len = nums.size();
        if (len == 0) return 0;
        vector<int> max[2];
        bool m[2];
        for (int i = 0; i < 2; ++i) max[i].resize(len);
        max[0][0] = max[1][0] = 1;
        m[0] = false;
        m[1] = true;

        for (int i = 1; i < len; ++i) {
            for (int j = 0; j < 2; ++j) {
                if (!m[j]) {  // now we desire a negative difference
                    if (nums[i - 1] < nums[i]) {  // is a desired negative difference
                        m[j] = true;  // next we will desire positive difference
                        max[j][i] = max[j][i - 1] + 1;
                    }
                    else {  // not a desired negative difference
                        max[j][i] = max[j][i - 1];
                    }
                }
                else {      // now we desire a positive difference 
                    if (nums[i - 1] > nums[i]) {
                        m[j] = false;
                        max[j][i] = max[j][i - 1] + 1;
                    }
                    else {
                        max[j][i] = max[j][i - 1];
                    }
                }
            }
        }
        return max[0][len - 1] > max[1][len - 1] ? max[0][len - 1] : max[1][len - 1];
    }
};

优化

看leetcode中的答案,发现自己的实现有点复杂,可以更简化。

用两个数组 up 和 down 分别记录最后的difference为positive和negative的最大长度. 两个数组相互依赖。具体见代码中注释。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int size = nums.size();

        if (size == 0) {
            return 0;
        }

        /*
         up[i] is the length of a longest wiggle subsequence of 
        {nums[0],...,nums[i]}, with a positive difference between its 
        last two numbers. This subsequence may or may not include nums[i] 
        and there may be several such subsequences (of the same length). 
        We call this a subsequence of type U.
         */
        vector<int> up(size, 0);

        /* 
        down[i] is the length of a longest wiggle subsequence of 
        {nums[0],...,nums[i]}, with a negative difference between its 
        last two numbers. This subsequence may or may not include nums[i] 
        and there may be several such subsequences (of the same length).
        We call this a subsequence of type D.
         */
        vector<int> down(size, 0);

        // At i=0, there is only one number and we can use it as a subsequence, 
        // i.e up[0]=down[0]=1
        up[0] = 1;
        down[0] = 1;
        for(int i=1; i<size; ++i){

            if (nums[i] > nums[i-1]) {
                /*
                 If nums[i] > nums[i-1], then we can use nums[i] to make a longer 
                 subsequence of type U
                    Proof: We consider a subsequence of type D in {0,...,i-1} 
                    (its length is down[i-1]).
                    Let N be the last number of this subsequence.
                    - If nums[i] > N, then we can add nums[i] to the subsequence 
                    and it gives us a longer valid subsequence of type U.
                    - If nums[i] <= N, then:
                    (1) N cannot be nums[i-1], because nums[i-1] < nums[i] <= N 
                    i.e. nums[i-1] < N
                    (2) We can replace N with nums[i-1] (we still have a valid
                    subsequence of type D since N >= nums[i] > nums[i-1] 
                    i.e. N > nums[i-1]), and then add nums[i] to the subsequence, 
                    and we have a longer subsequence of type U.
                    Therefore up[i] = down[i-1] + 1

                    There is no gain in using nums[i] to make a longer 
                    subsequence of type D.
                    Proof: Let N be the last number of a subsequence of type U
                    in {0,...,i-1}.
                    Assume we can use nums[i] to make a longer subsequence of type D. Then:
                    (1) N cannot be nums[i-1], otherwise we would not be able to use nums[i]
                    to make a longer subsequence of type D as nums[i] > nums[i-1]
                    (2) Necessarily nums[i] < N, and therefore nums[i-1] < N since nums[i-1] < nums[i].
                    But this means that we could have used nums[i-1] already to make a longer
                    subsequence of type D.
                    So even if we can use nums[i], there is no gain in using it, so we keep the old value of
                    down (down[i] = down[i-1])
                */
                up[i] = down[i-1] + 1;
                down[i] = down[i-1];
            }
            else if (nums[i] < nums[i-1]) {
                /** The reasoning is similar if nums[i] < nums[i-1] */
                down[i] = up[i-1] + 1;
                up[i] = up[i-1];
            }
            else {
                /** if nums[i] == nums[i-1], we cannot do anything more than what we did with
                     nums[i-1] so we just keep the old values of up and down
                */
                up[i] = up[i-1];
                down[i] = down[i-1];
            }
        }
        return max(up[size-1], down[size-1]);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值