紫书的例题,不错的题。可以用O(n)的空间one pass解决,因为有二分,时间应该是O(nlogn)。
基本思路是用当前递增子序列的起始元素去匹配之前递增子序列的末尾元素,如果起始元素 A[i] > 末尾元素A[j] 则两个子序列可以组合成新的子序列。
关键点:
- 每种长度的子序列只需保留末尾元素最小的一个,所以只需一个数组minR即可,index是长度,value是该长度的子序列的有末尾元素。
- 当找到一个新的子序列后,需要从这个子序列的开头开始遍历一遍,假设当前元素下标为k:1)以k开始的子序列(也即当前子序列的右半部份)可能可以匹配到新的子序列,因为A[k] > A[k-1]; 2) 以k结束的子序列子序列(也即当前子序列的左半部份)可能可以更新已有的子序列(长度一样,末尾元素更小)。换言之,我们还需要考虑当前子序列的子序列,而不是仅仅将当前找到的子序列当作整体处理。
上代码。一般的case可以过,提交两次都WA,也懒得调试了,主要看气质吧。
1 class UVa1471{ 2 public: 3 int FindLongestCombinedSubSeq(vector<int> &v){ 4 size_t len = v.size(); 5 // Index is the length of existing sub sequence, value is the rightmost value of the subsequence 6 vector<int> minR(len, RMAX); 7 int prev = INT_MIN, seq = 0, maxSeqLen = 0; 8 for(size_t i = 0; i < len; i++){ 9 if(v[i] > prev){ 10 seq++; 11 }else{ 12 int lo = i - seq; 13 for(int k = 0; k < seq; k++){ 14 int num = v[lo + k]; 15 // Consider right part of current sequence, num as left element, find previous sub sequcence which right end element < num. 16 int prevSeqLen = binSearch(minR, num); 17 maxSeqLen = max(maxSeqLen, prevSeqLen + seq - k); 18 // Consider left part of current sequence, num as right element, replace existing sub sequence if its righ end element > num. 19 int leftPartLen = k + 1; 20 minR[leftPartLen] = min(minR[leftPartLen], num); 21 } 22 seq = 1; 23 } 24 prev = v[i]; 25 } 26 int lo = len - seq; 27 for(int k = 0; k < seq; k++){ 28 int prevSeqLen = binSearch(minR, v[lo + k]); 29 maxSeqLen = max(maxSeqLen, prevSeqLen + seq - k); 30 } 31 return maxSeqLen; 32 } 33 34 private: 35 const int RMAX = 0x3f3f3f3f; 36 int binSearch(vector<int> &v, int k){ 37 size_t lo = 0, hi = v.size() - 1; 38 // invariant: the largest element < k is in [lo, hi] 39 while(lo < hi){ 40 size_t m = lo + (hi - lo + 1)/2; 41 if(v[m] >= k){ 42 hi = m - 1; 43 }else{ 44 lo = m; 45 } 46 } 47 return lo; 48 } 49 };
参考:http://blog.csdn.net/keshuai19940722/article/details/39297525