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
DP
题意:
给定一个序列,找出其中最长的子序列,使得该子序列相邻两个数的差值(后一个减去前一个)形成的序列正负交替(不能为0)。
分析:
- 该题可以用动态规划(DP)解决,我们作如下考虑:假设以i为最后一个元素的子序列中,wiggle sequence的最后一个值为负数的最优解为dec[i],最后一个值为整数的最优解为inc[i],则最优解max[i] = max{inc[i], dec[i]}。
- 下面分析最优子结构性质,对于dec[i],该子序列的前一个元素为j(j< i & nums[i] < nums[j]),则inc[j] = dec[i] - 1,若不然,设存在以j为最后一个元素且wiggle sequence中最后一个数为正数的子序列S个数为m, m > dec[i] - 1,则序列S + nums[i] 满足wiggle sequence,即存在以i为最后一个元素且wiggle sequence中最后一个数为负数的子序列S + nums[i],个数为 m + 1 > inc[j] + 1 = dec[i],这以假设dec[i]为以i为最后一个元素且wiggle sequence中最后一个数为负数的最优解矛盾。inc[i]的证明类似。
递推公式:
代码(c++, O( N2) ):
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
vector<int> inc(nums.size(), 1);
vector<int> dec(nums.size(), 1);
int maxs = (nums.size() == 0 ? 0 : 1);
for(int i = 1; i < nums.size(); i++){
for(int j = 0; j < i; j++){
if(nums[i] > nums[j] && dec[j] + 1 > inc[i]) inc[i] = dec[j] + 1;
if(nums[i] < nums[j] && inc[j] + 1 > dec[i]) dec[i] = inc[j] + 1;
}
maxs = max(maxs, max(inc[i], dec[i]));
}
return maxs;
}
};
Greedy
后来看了一下题目最后有一句话:Can you do it in O(n) time? 上面的DP虽然可以过,但是时间复杂度为O( N2) ,于是想了一个别的方法。
分析:
我们可以这样分析:假设合法的子序列中第一个数为正数(> 0),设该序列为 [x1,x2,x3,x4,...,xk] ,满足 nums[x2]−nums[x1]>0,nums[x4]<nums[x3]<0...(nums[xk]−nums[xk−1])∗(−1)logk+1>0 ,所以我们需要一个方向位ai来记录下一个nums[i] - nums[j]应该大于0还是小于0。可以使用贪心策略:从前往后选择合法的数加入子序列中,如果不合法,则将解序列中的最后一个数替换为当前的数。
证明:
- 假如部分子序列 [x1,x2,x3,x4,...,xm] 中,nums[x_m] > nums[x_{m-1}],那么接下来添加的数必须满足nums[x_i] < nums[x_m],如果不满足,则用i替换 xm ,替换后的序列为 [x1,x2,x3,x4,...,xm−1,i] ,因为nums[i] > nums[ xm ],所以nums[i] > nums[ xm−1 ],替换后的序列合法,
- 下面证明如果最优序列S中含有 [x1,x2,x3,x4,...,xm] ,一定也存在一个最优序列S’,包含 [x1,x2,x3,x4,...,xm−1,i] ,假设 j为S中第一个下标比 xm 大的数的下标,S = [x1,x2,x3,x4,...,xm,j] ⋃ S1 ,用i替换 xm,因为nums[j]<nums[ x_m ]<nums[i],所以S′= [x_1,x_2,x_3,x_4,…,x_m,i] ⋃ S1 合法,且|S’| = |S|,所以S’也是最优解。
代码(O( N) ):
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int firstinc, firstdec;
if(nums.size() == 0) return 0;
firstinc = firstdec = 1;
int ai, bi;
ai = 1, bi = -1; //初始化,ai交替取1和-1,ai*(nums[i] - nums[inc])用于判断差值是否正负交替
for(int i = 1; i < nums.size(); i++){
if(ai * (nums[i] - nums[i-1]) > 0){//合法
ai = -ai; //换方向
firstinc++;
}
if(bi * (nums[i] - nums[i-1]) > 0){//合法
bi = -bi;
firstdec++;
}
}
return max(firstinc, firstdec);
}
};