数组中的子序列类问题总结

这类问题一般是与数组中的子序列类问题有关。

1、数组中的最大子序列和(动态规划):

代码:

int find_max(const int a[],int n){
    int this_sum,max_sum,j;
    this_sum=max_sum=0;
    for(j=0;j<n;j++){
      this_sum+=a[j];
      if(this_sum>max_sum){
        max_sum=this_sum;
}else if(this_sum<0){
        this_sum=0;
}
}
     return max_sum;
}

思路:

  使用两个变量记录过程,一个记录当前过程,一个记录总过程。

2、递增三元组序列

题目:

Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.

Formally the function should:

Return true if there exists  i, j, k
such that  arr[i] <  arr[j] <  arr[k] given 0 ≤  i <  j <  k ≤  n-1 else return false.

Note: Your algorithm should run in O(n) time complexity and O(1) space complexity.

Example 1:

Input: [1,2,3,4,5]
Output: true

Example 2:

Input: [5,4,3,2,1]
Output: false

代码:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int m1 = INT_MAX, m2 = INT_MAX;
        for (auto a : nums) {
            if (m1 >= a) m1 = a;
            else if (m2 >= a) m2 = a;
            else return true;
        }
        return false;
    }
};

思路:

使用两个变量记录,根据数字的规律,从而找到符合条件的上升三元组。

3、最长上升子序列

题目:

Input: 
[10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is 
[2,3,7,101]
, therefore the length is 
4
. 
Note:

There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?

代码:

方法一——动态规划:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
         int len = nums.size();
    if (len == 0)return 0;
    vector<int> record(len + 1, 0);
    int res = 0;
    record[0] = INT_MIN;
    for (int i = 1; i <= len; i++) {
        record[i] = 1;
        for (int j = 1; j < i; j++) {
            if (nums[j - 1] < nums[i - 1]) {
                record[i] = max(record[i], record[j] + 1);      
            }
        }
         res = max(res, record[i]);
    }
    return res;
  }
};

方法二——二分法,N(logN),维持一个队列:

// NLogN solution from geek for geek
int lengthOfLIS(vector<int>& nums) {
    if (nums.empty()) { return 0; } 
    vector<int> tail;  // keep tail value of each possible len
    tail.push_back(nums[0]);
    for (auto n : nums) {
        if (n <= tail[0]) {
            tail[0] = n;
        } else if (n > tail.back()) { // large than the tail of current largest len 
            tail.push_back(n);
        } else {  // find smallest one which is >= n
            int left = 0; 
            int right = tail.size()-1;
            int res = left;
            while(left <= right) {
                int mid = left + (right-left)/2;
                if (tail[mid] >= n) {
                    res = mid;
                    right = mid-1;
                } else { // tail[mid] < n
                    left = mid+1;
                }
            }
            tail[res] = n;
        }
    }
    return tail.size();
}

想法:因为最后出现的是长度嘛,所以出现冲突逐渐替换,不会影响结果,˙只有新元素大于tail的时候,才会增加tail的size。

4、Distinct Subsequences II

题目:

Given a string S, count the number of distinct, non-empty subsequences of S .

Since the result may be large, return the answer modulo 10^9 + 7.

Example 1:

Input: "abc"
Output: 7
Explanation: The 7 distinct subsequences are "a", "b", "c", "ab", "ac", "bc", and "abc".

Example 2:

Input: "aba"
Output: 6
Explanation: The 6 distinct subsequences are "a", "b", "ab", "ba", "aa" and "aba".

Example 3:

Input: "aaa"
Output: 3
Explanation: The 3 distinct subsequences are "a", "aa" and "aaa".

Note:

  1. S contains only lowercase letters.
  2. 1 <= S.length <= 2000

代码:

class Solution {
public:
    int distinctSubseqII(string S) {
        const int N = S.length(), M = 1e9 + 7;
        int res = 0; 
        vector<int> end(26, 0);
        for (const auto c : S) {
            int i = c - 'a';
            int added = (res - end[i] + 1) % M;
            res = (res + added) % M;
            end[i] = (end[i] + added) % M;
        }
        return (res+M) % M;
    }
};

想法:

根据题目意思,寻找以i结尾的个数和以i+1结尾的个数之间的关系。可借鉴的一点是最后加M然后模M。目的是防止res溢出了。

5、Longest Common Subsequence

题目:

Given two strings text1 and text2, return the length of their longest common subsequence.

subsequence of a string is a new string generated from the original string with some characters(can be none) deleted without changing the relative order of the remaining characters. (eg, "ace" is a subsequence of "abcde" while "aec" is not). A common subsequence of two strings is a subsequence that is common to both strings.

If there is no common subsequence, return 0.

Example 1:

Input: text1 = "abcde", text2 = "ace" 
Output: 3  
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.

代码:

方法一——二维数组动态规划:

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int len1=text1.length();
    int len2=text2.length();
    vector<vector<int>> record(len1+1,vector<int>(len2+1,0));
    for(int i=1;i<=len1;i++){
        for(int j=1;j<=len2;j++){
            if(text1[i-1]==text2[j-1]){
                record[i][j]=record[i-1][j-1]+1;
            }
            record[i][j]=max(record[i][j],record[i-1][j]);
            record[i][j]=max(record[i][j],record[i][j-1]);
        }
    }
    return record[len1][len2];
    }
};

想法:常规思路,找到动态规划关系递推式。注意,C++动态分配的二维数组要比vector<vector>>更快,且更节省空间。

6、Constrained Subsequence Sum

题目:

Given an integer array nums and an integer k, return the maximum sum of a non-empty subsequence of that array such that for every two consecutive integers in the subsequence, nums[i] and nums[j], where i < j, the condition j - i <= k is satisfied.

A subsequence of an array is obtained by deleting some number of elements (can be zero) from the array, leaving the remaining elements in their original order.

 

Example 1:

Input: nums = [10,2,-10,5,20], k = 2
Output: 37
Explanation: The subsequence is [10, 2, 5, 20].
Example 2:

Input: nums = [-1,-2,-3], k = 1
Output: -1
Explanation: The subsequence must be non-empty, so we choose the largest number.
Example 3:

Input: nums = [10,-2,-10,-5,20], k = 2
Output: 23
Explanation: The subsequence is [10, -2, -5, 20].
 

Constraints:

1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104

代码:

class Solution {
public:
    int constrainedSubsetSum(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> f(n);
        f[0] = nums[0];
        deque<int> q;
        q.push_back(0);
        int ans = nums[0];
        for (int i = 1; i < n; ++i) {
            while (!q.empty() && i - q.front() > k) {
                q.pop_front();
            }
            f[i] = max(f[q.front()], 0) + nums[i];
            ans = max(ans, f[i]);
            while (!q.empty() && f[i] >= f[q.back()]) {
                q.pop_back();
            }
            q.push_back(i);
        }
        return ans;
    }
};

思路:
不维护队列的时间复杂度为O(nk),维护队列的时间复杂度为O(n),两者的空间复杂度都是O(n)。

7、Longest Arithmetic Subsequence

题目:

Given an array A of integers, return the length of the longest arithmetic subsequence in A.

Recall that a subsequence of A is a list A[i_1], A[i_2], ..., A[i_k] with 0 <= i_1 < i_2 < ... < i_k <= A.length - 1, and that a sequence B is arithmetic if B[i+1] - B[i] are all the same value (for 0 <= i < B.length - 1).

Example 1:

Input: A = [3,6,9,12]
Output: 4
Explanation: 
The whole array is an arithmetic sequence with steps of length = 3.

Example 2:

Input: A = [9,4,7,2,10]
Output: 3
Explanation: 
The longest arithmetic subsequence is [4,7,10].

Example 3:

Input: A = [20,1,15,3,10,5,8]
Output: 4
Explanation: 
The longest arithmetic subsequence is [20,15,10,5].

Constraints:

  • 2 <= A.length <= 1000
  • 0 <= A[i] <= 500

代码:

class Solution {
public:
    int longestArithSeqLength(vector<int>& A) {
    int len=A.size(),res=0;
    int ** record=new int*[1000];
    for(int i=0;i<1000;i++){
        record[i]=new int[1001];
        for(int j=0;j<1001;j++){
            record[i][j]=0;
        }
    }
    for(int i=1;i<len;i++){
        for(int j=0;j<i;j++) {
            int k = A[i] - A[j] + 500;
            record[i][k] = record[j][k] + 1;
            res = max(res, record[i][k]);
        }
    }
    return res+1;
    }
};

想法:

找到动态规划structure,然后找到递推关系。

8、Sum of Subsequence Widths

题目:

Given an array of integers A, consider all non-empty subsequences of A.

For any sequence S, let the width of S be the difference between the maximum and minimum element of S.

Return the sum of the widths of all subsequences of A. 

As the answer may be very large, return the answer modulo 10^9 + 7.

Example 1:

Input: [2,1,3]
Output: 6
Explanation:
Subsequences are [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3].
The corresponding widths are 0, 0, 0, 1, 1, 2, 2.
The sum of these widths is 6.

Note:

  • 1 <= A.length <= 20000
  • 1 <= A[i] <= 20000

代码:

class Solution {
public:
    int sumSubseqWidths(vector<int>& A) {
        long res = 0, n = A.size(), M = 1e9 + 7, c = 1;
		sort(A.begin(), A.end());
		for (int i = 0; i < n; ++i) {
			res = (res + A[i] * c - A[n - i - 1] * c) % M;
			c = (c << 1) % M;
		}
		return res;
    }
};

思路:

这道题给了我们一个数组,并且定义了一种子序列的宽度,就是非空子序列中最大值和最小值的差值,让我们算出所有的子序列的宽度之和,而且提示了结果可能是个超大数,要对1e9+7取余。由于要求是子序列,所以只关心最大值最小值。阶梯只要要知道的是,一个长度为n的数组,共有多少个子序列,如果算上空集的话,共有2^n个;再给数组排序之后,对于其中任意一个数字A[i],其前面共有i个数是小于等于A[i]的,这i个数字共有2^i个子序列,它们加上A[i]都可以组成一个新的非空子序列,并且A[I]是这里面最大的数字,那么在宽度计算的时候,就要加上A[i]x(2^i),同理,A[i]后面还有n-1-i个数字是大于等于它的,后面可以形成2^(n-1-i)个子序列,每个加上A[i]就都是一个新的非空子序列,同时A[i]是这些子序列中最小的一个,那么结果中就要减去A[i]x(2^(n-1-i))。

找规律,结合动态规划。

9、Longest Uncommon Subsequence I

题目:

Given two strings a and b, find the length of the longest uncommon subsequence between them.

subsequence of a string s is a string that can be obtained after deleting any number of characters from s. For example, "abc" is a subsequence of "aebdc" because you can delete the underlined characters in "aebdc" to get "abc". Other subsequences of "aebdc" include "aebdc""aeb", and "" (empty string).

An uncommon subsequence between two strings is a string that is a subsequence of one but not the other.

Return the length of the longest uncommon subsequence between a and b. If the longest uncommon subsequence doesn't exist, return -1.

Example 1:

Input: a = "aba", b = "cdc"
Output: 3
Explanation: One longest uncommon subsequence is "aba" because "aba" is a subsequence of "aba" but not "cdc".
Note that "cdc" is also a longest uncommon subsequence.

Example 2:

Input: a = "aaa", b = "bbb"
Output: 3
Explanation: The longest uncommon subsequences are "aaa" and "bbb".

Example 3:

Input: a = "aaa", b = "aaa"
Output: -1
Explanation: Every subsequence of string a is also a subsequence of string b. Similarly, every subsequence of string b is also a subsequence of string a.

Constraints:

  • 1 <= a.length, b.length <= 100
  • a and b consist of lower-case English letters.

代码:

class Solution {
public:
    int findLUSlength(string a, string b) {
        
        return a == b ? -1 : max(a.size(), b.size());
    
    }
};

思路:

两个字符串的情况很少,如果两个字符串相等,那么一定没有非共同子序列,反之,如果两个字符串不等,那么较长的那个字符串就是最长非共同子序列。

9、Longest Uncommon Subsequence II

题目:

Given a list of strings, you need to find the longest uncommon subsequence among them. The longest uncommon subsequence is defined as the longest subsequence of one of these strings and this subsequence should not be any subsequence of the other strings.

subsequence is a sequence that can be derived from one sequence by deleting some characters without changing the order of the remaining elements. Trivially, any string is a subsequence of itself and an empty string is a subsequence of any string.

The input will be a list of strings, and the output needs to be the length of the longest uncommon subsequence. If the longest uncommon subsequence doesn't exist, return -1.

Example 1:

Input: "aba", "cdc", "eae"
Output: 3

Note:

  1. All the given strings' lengths will not exceed 10 .
  2. The length of the given list will be in the range of [2, 50] .

代码:

方法一——暴力搜索:

class Solution {
public:
    int findLUSlength(vector<string>& strs) {
        int res = -1, j = 0, n = strs.size();
        for (int i = 0; i < n; ++i) {
            for (j = 0; j < n; ++j) {
                if (i == j) continue;
                if (checkSubs(strs[i], strs[j])) break;
            }
            if (j == n) res = max(res, (int)strs[i].size());
        }
        return res;
    }
    int checkSubs(string subs, string str) {
        int i = 0;
        for (char c : str) {
            if (c == subs[i]) ++i;
            if (i == subs.size()) break;
        } 
        return i == subs.size();
    }
};

思路:

遍历所有的字符串,对于每个遍历到的字符串,再和所有的其他的字符串比较,看是不是某一个字符串的子序列,如果都不是的话,那么当前字符串就是一个非共同子序列,用其长度来更新结果res。

方法二——先排序再搜索:

class Solution {
public:
    int findLUSlength(vector<string>& strs) {
        int n = strs.size();
        unordered_set<string> s;
        sort(strs.begin(), strs.end(), [](string a, string b){
            if (a.size() == b.size()) return a > b;
            return a.size() > b.size();
        });
        for (int i = 0; i < n; ++i) {
            if (i == n - 1 || strs[i] != strs[i + 1]) {
                bool found = true;
                for (auto a : s) {
                    int j = 0;
                    for (char c : a) {
                        if (c == strs[i][j]) ++j;
                        if (j == strs[i].size()) break;
                    }
                    if (j == strs[i].size()) {
                        found = false;
                        break;
                    }
                }
                if (found) return strs[i].size();
            }
            s.insert(strs[i]);
        }
        return -1;
    }
};

思路:

        首先我们给字符串按长度来排序,将长度大的放到前面,这样我们如果找到了非共同子序列,那么直接返回其长度即可,因为当前找到的肯定是最长的。然后我们用一个集合来记录已经遍历过的字符串,利用集合的去重复特性,这样在有大量的重复字符串的时候可以提高效率,然后我们开始遍历字符串,对于当前遍历到的字符串,我们和集合中的所有字符串相比,看其是否是某个的子序列,如果都不是,说明当前的就是最长的非共同子序列。注意如果当前的字符串是集合中某个字符串的子序列,那么直接break出来,不用再和其他的比较了,这样在集合中有大量的字符串时可以提高效率,最后别忘了将遍历过的字符串加入集合中。

10、Longest Palindromic Subsequence

题目:

Given a string s, find the longest palindromic subsequence's length in s. You may assume that the maximum length of s is 1000.

Example 1:
Input:

"bbbab"

Output:

4

One possible longest palindromic subsequence is "bbbb".

Example 2:
Input:

"cbbd"

Output:

2

One possible longest palindromic subsequence is "bb".

Constraints:

  • 1 <= s.length <= 1000
  • s consists only of lowercase English letters.

代码:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int len=s.length();
    vector<vector<int>> record(len,vector<int>(len,0));
    for(int k=0;k<len;k++){
        for(int j=0;j<len-k;j++){
            int i=j+k;
            if(s[j]==s[i]){
                if(i-j<=1)record[j][i]=i-j+1;
                else{
                    record[j][i]=record[j+1][i-1]+2;
                }
            }else{
                record[j][i]=max(record[j][i],record[j][i-1]);
                record[j][i]=max(record[j][i],record[j+1][i]);
            }
        }
    }
    return record[0][len-1];
    }
};

想法:

找对动态规划递推关系式,找对遍历顺序(注意,这是回文串,所以用矩阵乘法那种方式去遍历)。

11、Increasing Subsequences

题目:

Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2.

Example:

Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

Constraints:

  • The length of the given array will not exceed 15.
  • The range of integer in the given array is [-100,100].
  • The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.

代码:

方法一——:

class Solution {
public:
    vector<vector<int>> res;
void trans(vector<int>& paths,int currentindex,vector<int>&nums){
    if(currentindex>=nums.size()){
        if(paths.size()>=2)res.push_back(paths);
        return;
    }
    if(nums[currentindex]>=paths.back()){
        paths.push_back(nums[currentindex]);
        trans(paths,currentindex+1,nums);
        paths.pop_back();
    }
    trans(paths,currentindex+1,nums);

}
vector<vector<int>> findSubsequences(vector<int>& nums) {
    int len=nums.size();
    for(int i=0;i<len-1;i++){
        vector<int> path={nums[i]};
        trans(path,i+1,nums);
    }
    sort(res.begin(),res.end());
    res.erase(unique(res.begin(),res.end()),res.end());
    return res;
}
};

思路:

采用深度优先搜索的思路,以数组中的每个元素为起点,向后进行深度优先搜索。注意,搜索的过程中有两条路,如果当前元素大于等于paths的末尾元素,那么加入或者不加入,向后遍历;否则,不加入,向后遍历;

还要注意临界条件是怎么返回的,只要currentindex>=nums.size(),就该return,但是,只有paths.size()>=2的时候,我们才将paths放入到res中。还有一个需要注意的点是,因为遍历过程中可能会产生重复的路径,所以需要sort和erase。

如果数组集合叠加,用动态规划太麻烦,可以用DFS。

方法二——:

代码:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        set<vector<int>> res;
        vector<int> out;
        helper(nums, 0, out, res);
        return vector<vector<int>>(res.begin(), res.end());
    }
    void helper(vector<int>& nums, int start, vector<int>& out, set<vector<int>>& res) {
        if (out.size() >= 2) res.insert(out);
        for (int i = start; i < nums.size(); ++i) {
            if (!out.empty() && out.back() > nums[i]) continue;
            out.push_back(nums[i]);
            helper(nums, i + 1, out, res);
            out.pop_back();
        }
    }
};

思路:

既有动态规划的思想,又有递归的思想,同时运用了set自动去除重复项的机制;

方法三——:

代码:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> out;
        helper(nums, 0, out, res);
        return res;
    }
    void helper(vector<int>& nums, int start, vector<int>& out, vector<vector<int>>& res) {
        if (out.size() >= 2) res.push_back(out);
        unordered_set<int> st;
        for (int i = start; i < nums.size(); ++i) {
            if ((!out.empty() && out.back() > nums[i]) || st.count(nums[i])) continue;
            out.push_back(nums[i]);
            st.insert(nums[i]);
            helper(nums, i + 1, out, res);
            out.pop_back();
        }
    }
};

思路:在递归过程中进行去重处理

方法四——:

代码:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        set<vector<int>> res;
        vector<vector<int>> cur(1);
        for (int i = 0; i < nums.size(); ++i) {
            int n = cur.size();
            for (int j = 0; j < n; ++j) {
                if (!cur[j].empty() && cur[j].back() > nums[i]) continue;
                cur.push_back(cur[j]);
                cur.back().push_back(nums[i]);
                if (cur.back().size() >= 2) res.insert(cur.back());
            }
        }
        return vector<vector<int>>(res.begin(), res.end());
    }
};

思路:递归的方法

方法五——:

代码:

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<vector<int>> res, cur(1);
        unordered_map<int, int> m;
        for (int i = 0; i < nums.size(); ++i) {
            int n = cur.size(), start = m[nums[i]];
            m[nums[i]] = n;
            for (int j = start; j < n; ++j) {
                if (!cur[j].empty() && cur[j].back() > nums[i]) continue;
                cur.push_back(cur[j]);
                cur.back().push_back(nums[i]);
                if (cur.back().size() >= 2) res.push_back(cur.back());
            }
        }
        return res;
    }
};

思路:

我们来看不用 TreeSet 的方法,使用一个 HashMap 来建立每个数字对应的遍历起始位置,默认都是0,然后在遍历的时候先取出原有值当作遍历起始点,然后更新为当前位置,如果某个数字之前出现过,那么取出的原有值就不是0,而是之前那个数的出现位置,这样就不会产生重复了。

12、Number of Longest Increasing Subsequence

题目:

Given an integer array nums, return the number of longest increasing subsequences.

Notice that the sequence has to be strictly increasing.

Example 1:

Input: nums = [1,3,5,4,7]
Output: 2
Explanation: The two longest increasing subsequences are [1, 3, 4, 7] and [1, 3, 5, 7].

Example 2:

Input: nums = [2,2,2,2,2]
Output: 5
Explanation: The length of longest continuous increasing subsequence is 1, and there are 5 subsequences' length is 1, so output 5.

代码:

方法一——复杂的动态规划:

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        int len=nums.size();
    if(len==1)return 1;
    vector<vector<int>> v(len+1,vector<int>(2000,0));
    vector<int> l(len+1,0);
    nums.insert(nums.begin(),0);
    int maxlen=0;
    for(int i=1;i<=len;i++){
        v[i][0]=1;
        l[i]=1;
    }
    for(int i=1;i<=len;i++){
        for(int j=0;j<i;j++){
            if(nums[i]>nums[j]){
                l[i]=max(l[i],l[j]+1);
                maxlen=max(maxlen,l[i]);
                if(l[j]!=0)v[i][l[j]]+=v[j][l[j]-1];
            }
        }
    }
    nums.erase(nums.begin());
    int result=0;
    for(int k=maxlen;k<=len;k++){
        result+=v[k][maxlen-1];
    }
    return result;
    }
};

思路:两个动态规划的结合,较复杂。

方法二——:

代码:

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        int res = 0, mx = 0, n = nums.size();
        vector<int> len(n, 1), cnt(n, 1);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (nums[i] <= nums[j]) continue;
                if (len[i] == len[j] + 1) cnt[i] += cnt[j];
                else if (len[i] < len[j] + 1) {
                    len[i] = len[j] + 1;
                    cnt[i] = cnt[j];
                }
            }
            if (mx == len[i]) res += cnt[i];
            else if (mx < len[i]) {
                mx = len[i];
                res = cnt[i];
            }
        }
        return res;
    }
};

思路:其实这道题在设计 DP 数组的时候有个坑,如果将 dp[i] 定义为到i位置的最长子序列的个数的话,则递推公式不好找。但是如果将 dp[i] 定义为以 nums[i] 为结尾的递推序列的个数的话,再配上这些递推序列的长度,将会比较容易的发现递推关系。这里用 len[i] 表示以 nums[i] 为结尾的递推序列的长度,用 cnt[i] 表示以 nums[i] 为结尾的递推序列的个数,初始化都赋值为1,只要有数字,那么至少都是1。然后遍历数组,对于每个遍历到的数字 nums[i],再遍历其之前的所有数字 nums[j],当 nums[i] 小于等于 nums[j] 时,不做任何处理,因为不是递增序列。反之,则判断 len[i] 和 len[j] 的关系,如果 len[i] 等于 len[j] + 1,说明 nums[i] 这个数字可以加在以 nums[j] 结尾的递增序列后面,并且以 nums[j] 结尾的递增序列个数可以直接加到以 nums[i] 结尾的递增序列个数上。如果 len[i] 小于 len[j] + 1,说明找到了一条长度更长的递增序列,那么此时将 len[i] 更新为 len[j]+1,并且原本的递增序列都不能用了,直接用 cnt[j] 来代替。在更新完 len[i] 和 cnt[i] 之后,要更新 mx 和结果 res,如果 mx 等于 len[i],则把 cnt[i] 加到结果 res 之上;如果 mx 小于 len[i],则更新 mx 为 len[i],更新结果 res 为 cnt[i]。

13、Length of Longest Fibonacci Subsequence

题目:

A sequence X1, X2, ..., Xn is Fibonacci-like if:

  • n >= 3
  • Xi + Xi+1 = Xi+2 for all i + 2 <= n

Given a strictly increasing array arr of positive integers forming a sequence, return the length of the longest Fibonacci-like subsequence of arr. If one does not exist, return 0.

A subsequence is derived from another sequence arr by deleting any number of elements (including none) from arr, without changing the order of the remaining elements. For example, [3, 5, 8] is a subsequence of [3, 4, 5, 6, 7, 8].

Example 1:

Input: arr = [1,2,3,4,5,6,7,8]
Output: 5
Explanation: The longest subsequence that is fibonacci-like: [1,2,3,5,8].

Example 2:

Input: arr = [1,3,7,11,12,14,18]
Output: 3
Explanation: The longest subsequence that is fibonacci-like: [1,11,12], [3,11,14] or [7,11,18].

Constraints:

  • 3 <= arr.length <= 1000
  • 1 <= arr[i] < arr[i + 1] <= 109

代码:

方法一——二维数组动态规划(超时):

class Solution {
public:
    int lenLongestFibSubseq(vector<int>& arr) {
        int len=arr.size(),res=0;
        vector<vector<int>> d(arr[len-1]+1,vector<int>(arr[len-1]+1,0));
    
        for(int i=1;i<len;i++){
            for(int j=0;j<i;j++){
                d[arr[i]][arr[j]]=1;
                if(d[arr[j]][arr[i]-arr[j]]!=0){
                    d[arr[i]][arr[j]]=d[arr[j]][arr[i]-arr[j]]+1;
                }
                res=max(res,d[arr[i]][arr[j]]);
            }
        }
        return res+1<3?0:res+1;
    }
};

方法二——暴力直接遍历:

class Solution {
public:
    int lenLongestFibSubseq(vector<int>& A) {
        int res = 0, n = A.size();
        unordered_set<int> st(A.begin(), A.end());
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int a = A[i], b = A[j], cnt = 2;
                while (st.count(a + b)) {
                    b = a + b;
                    a = b - a;
                    ++cnt;
                }
                res = max(res, cnt);
            }
        }
        return (res > 2) ? res : 0;
    }
};

思路:巧妙点在于把vector转变为set,这样可以直接去判断有没有元素存在。

方法三——另外一种嵌套少的动态规划:

class Solution {
public:
    int lenLongestFibSubseq(vector<int>& A) {
        int res = 0, n = A.size();
        unordered_map<int, int> m;
        vector<vector<int>> dp(n, vector<int>(n));
        for (int i = 0; i < n; ++i) {
            m[A[i]] = i;
            for (int j = 0; j < i; ++j) {
                int k = m.count(A[i] - A[j]) ? m[A[i] - A[j]] : -1;
                dp[j][i] = (A[i] - A[j] < A[j] && k >= 0) ? (dp[k][j] + 1) : 2;
                res = max(res, dp[j][i]);
            }
        }
        return (res > 2) ? res : 0;
    }
};

思路:必要的时候需要使用count的时候真的可以想办法使用unordered_set或者unordered_map。

14、Find the Most Competitive Subsequence

题目:

Given an integer array nums and a positive integer k, return the most competitive subsequence of nums of size k.

An array's subsequence is a resulting sequence obtained by erasing some (possibly zero) elements from the array.

We define that a subsequence a is more competitive than a subsequence b (of the same length) if in the first position where a and b differ, subsequence a has a number less than the corresponding number in b. For example, [1,3,4] is more competitive than [1,3,5] because the first position they differ is at the final number, and 4 is less than 5.

Example 1:

Input: nums = [3,5,2,6], k = 2
Output: [2,6]
Explanation: Among the set of every possible subsequence: {[3,5], [3,2], [3,6], [5,2], [5,6], [2,6]}, [2,6] is the most competitive.

Example 2:

Input: nums = [2,4,3,3,5,4,9,6], k = 4
Output: [2,3,3,4]

Constraints:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 109
  • 1 <= k <= nums.length

代码:

class Solution {
    public int[] mostCompetitive(int[] nums, int k) {
        int []res=new int[k];
        int idx=0,rem=nums.length-k;
        for(int i=0;i<nums.length;i++){
            while(idx>0&&res[idx-1]>nums[i]&&rem>0){
                idx--;
                rem--;
            }
            if(idx<res.length){
                res[idx++]=nums[i];
            }else{
                rem--;
            }
        }
        return res;
    }
}

想法:

思路是单调栈。求长k的子序列相当于在A里删掉l A − k 个数字。我们用一个变量r标记还可以删多少个数字,一开始赋值为l A − k。接着开一个从栈底到栈顶严格单调上升的栈,如果栈非空并且栈顶大于新来的数,并且还能删数字(指r > 0成立),则出栈,并将r减1,直到栈空或者栈顶小于等于新来的数或者r = 0 这时没法删了),接着将新来的数入栈(如果栈满,说明新来的数无法入栈,则多删一个数,将r减去1)。最后栈底到栈顶的数组即为所求。

精髓是,通过限制可以删的数字的个数来控制后面小的数字的移动位置,逐渐向前移动。

15、 Longest Arithmetic Subsequence of Given Difference

题目:

Given an integer array arr and an integer difference, return the length of the longest subsequence in arr which is an arithmetic sequence such that the difference between adjacent elements in the subsequence equals difference.

Example 1:

Input: arr = [1,2,3,4], difference = 1
Output: 4
Explanation: The longest arithmetic subsequence is [1,2,3,4].

Example 2:

Input: arr = [1,3,5,7], difference = 1
Output: 1
Explanation: The longest arithmetic subsequence is any single element.

Example 3:

Input: arr = [1,5,7,8,5,3,4,2,1], difference = -2
Output: 4
Explanation: The longest arithmetic subsequence is [7,5,3,1].

Constraints:

  • 1 <= arr.length <= 10^5
  • -10^4 <= arr[i], difference <= 10^4
 

代码:

方法一——暴力,O(n^2):

class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) {
        int res=1;int len=arr.size();
    vector<int> v(len,1);
    for(int i=0;i<len;i++){
        for(int j=i-1;j>=0;j--){
            if(arr[i]-arr[j]==difference){
                v[i]=max(v[i],v[j]+1);
                res=max(v[i],res);
                break;
            }
            
        }
    }
    return res;
    }
};

方法二——结合map:

class Solution
{
public:
    int longestSubsequence(vector<int> &arr, int difference)
    {

        unordered_map<int, int> forwards;
        int res = 0;
        for (auto item : arr)
        {
            if (forwards.count(item - difference))
            {
                forwards[item] = forwards[item - difference] + 1;
                res = max(res, forwards[item]);
            }
            else
            {
                forwards[item] = 1;
            }
        }
        return res == 0 ? 1 : res;
    }
};

思路:不要避免使用任何一种数据结构,只要他有所帮助,就可以使用。map,以末尾数字为key,以当前符合的子序列长度为值。map,map,map,是核心。

16、Max Dot Product of Two Subsequences

题目:

Given two arrays nums1 and nums2.

Return the maximum dot product between non-empty subsequences of nums1 and nums2 with the same length.

A subsequence of a array is a new array which is formed from the original array by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, [2,3,5] is a subsequence of [1,2,3,4,5] while [1,5,3] is not).

Example 1:

Input: nums1 = [2,1,-2,5], nums2 = [3,0,-6]
Output: 18
Explanation: Take subsequence [2,-2] from nums1 and subsequence [3,-6] from nums2.
Their dot product is (2*3 + (-2)*(-6)) = 18.

Example 2:

Input: nums1 = [3,-2], nums2 = [2,-6,7]
Output: 21
Explanation: Take subsequence [3] from nums1 and subsequence [7] from nums2.
Their dot product is (3*7) = 21.

Example 3:

Input: nums1 = [-1,-1], nums2 = [1,1]
Output: -1
Explanation: Take subsequence [-1] from nums1 and subsequence [1] from nums2.
Their dot product is -1. 

Constraints:

  • 1 <= nums1.length, nums2.length <= 500
  • -1000 <= nums1[i], nums2[i] <= 1000
 

代码:

class Solution {
public:
    int maxDotProduct(vector<int>& nums1, vector<int>& nums2) {
       int len1=nums1.size(),len2=nums2.size();
    vector<vector<int>> v(len1,vector<int>(len2,INT_MIN));
    v[0][0]=nums1[0]*nums2[0];
    for(int i=1;i<len1;i++){
        v[i][0]=max(v[i-1][0],nums1[i]*nums2[0]);
    }
    for(int i=1;i<len2;i++){
        v[0][i]=max(v[0][i-1],nums1[0]*nums2[i]);
    }
    for(int i=1;i<len1;i++){
        for(int j=1;j<len2;j++){
            int temp=nums1[i]*nums2[j];
            v[i][j]=max(v[i-1][j],v[i][j]);
            v[i][j]=max(v[i][j],v[i][j-1]);
            if(v[i-1][j-1]>0){
                v[i][j]=max(v[i-1][j-1]+temp,v[i][j]);    
            }else{
                v[i][j]=max(temp,v[i][j]);
            }
            
        }
    }
    return v[len1-1][len2-1]; 
    }
};

思路:经典的动态规划,注意一点就这个序列是可以从中间开始的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值