Welcome to Subscribe On Youtube

673. Number of Longest Increasing Subsequence

Description

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 the longest increasing subsequence is 1, and there are 5 increasing subsequences of length 1, so output 5.

 

Constraints:

  • 1 <= nums.length <= 2000
  • -106 <= nums[i] <= 106

Solutions

Solution 1: Dynamic Programming

We define $f[i]$ as the length of the longest increasing subsequence ending with $nums[i]$, and $cnt[i]$ as the number of longest increasing subsequences ending with $nums[i]$. Initially, $f[i]=1$, $cnt[i]=1$. Also, we define $mx$ as the length of the longest increasing subsequence, and $ans$ as the number of longest increasing subsequences.

For each $nums[i]$, we enumerate all elements $nums[j]$ in $nums[0:i-1]$. If $nums[j] < nums[i]$, then $nums[i]$ can be appended after $nums[j]$ to form a longer increasing subsequence. If $f[i] < f[j] + 1$, it means the length of the longest increasing subsequence ending with $nums[i]$ has increased, so we need to update $f[i]=f[j]+1$ and $cnt[i]=cnt[j]$. If $f[i]=f[j]+1$, it means we have found a longest increasing subsequence with the same length as before, so we need to increase $cnt[i]$ by $cnt[j]$. Then, if $mx < f[i]$, it means the length of the longest increasing subsequence has increased, so we need to update $mx=f[i]$ and $ans=cnt[i]$. If $mx=f[i]$, it means we have found a longest increasing subsequence with the same length as before, so we need to increase $ans$ by $cnt[i]$.

Finally, we return $ans$.

The time complexity is $O(n^2)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $nums$.

Solution 2: Binary Indexed Tree

We can use a binary indexed tree to maintain the length and count of the longest increasing subsequence in the prefix interval. We remove duplicates from the array $nums$ and sort it to get the array $arr$. Then we enumerate each element $x$ in $nums$, find the position $i$ of $x$ in the array $arr$ by binary search, then query the length and count of the longest increasing subsequence in $[1,i-1]$, denoted as $v$ and $cnt$, then update the length and count of the longest increasing subsequence in $[i]$ to $v+1$ and $\max(cnt,1)$. Finally, we query the length and count of the longest increasing subsequence in $[1,m]$, where $m$ is the length of the array $arr$, which is the answer.

The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the array $nums$.

  • class BinaryIndexedTree {
        private int n;
        private int[] c;
        private int[] d;
    
        public BinaryIndexedTree(int n) {
            this.n = n;
            c = new int[n + 1];
            d = new int[n + 1];
        }
    
        public void update(int x, int v, int cnt) {
            while (x <= n) {
                if (c[x] < v) {
                    c[x] = v;
                    d[x] = cnt;
                } else if (c[x] == v) {
                    d[x] += cnt;
                }
                x += x & -x;
            }
        }
    
        public int[] query(int x) {
            int v = 0, cnt = 0;
            while (x > 0) {
                if (c[x] > v) {
                    v = c[x];
                    cnt = d[x];
                } else if (c[x] == v) {
                    cnt += d[x];
                }
                x -= x & -x;
            }
            return new int[] {v, cnt};
        }
    }
    
    public class Solution {
        public int findNumberOfLIS(int[] nums) {
            // int[] arr = Arrays.stream(nums).distinct().sorted().toArray();
            int[] arr = nums.clone();
            Arrays.sort(arr);
            int m = arr.length;
            BinaryIndexedTree tree = new BinaryIndexedTree(m);
            for (int x : nums) {
                int i = Arrays.binarySearch(arr, x) + 1;
                int[] t = tree.query(i - 1);
                int v = t[0];
                int cnt = t[1];
                tree.update(i, v + 1, Math.max(cnt, 1));
            }
            return tree.query(m)[1];
        }
    }
    
  • class BinaryIndexedTree {
    private:
        int n;
        vector<int> c;
        vector<int> d;
    
    public:
        BinaryIndexedTree(int n)
            : n(n)
            , c(n + 1, 0)
            , d(n + 1, 0) {}
    
        void update(int x, int v, int cnt) {
            while (x <= n) {
                if (c[x] < v) {
                    c[x] = v;
                    d[x] = cnt;
                } else if (c[x] == v) {
                    d[x] += cnt;
                }
                x += x & -x;
            }
        }
    
        pair<int, int> query(int x) {
            int v = 0, cnt = 0;
            while (x > 0) {
                if (c[x] > v) {
                    v = c[x];
                    cnt = d[x];
                } else if (c[x] == v) {
                    cnt += d[x];
                }
                x -= x & -x;
            }
            return {v, cnt};
        }
    };
    
    class Solution {
    public:
        int findNumberOfLIS(vector<int>& nums) {
            vector<int> arr = nums;
            sort(arr.begin(), arr.end());
            arr.erase(unique(arr.begin(), arr.end()), arr.end());
            int m = arr.size();
            BinaryIndexedTree tree(m);
            for (int x : nums) {
                auto it = lower_bound(arr.begin(), arr.end(), x);
                int i = distance(arr.begin(), it) + 1;
                auto [v, cnt] = tree.query(i - 1);
                tree.update(i, v + 1, max(cnt, 1));
            }
            return tree.query(m).second;
        }
    };
    
  • class BinaryIndexedTree:
        __slots__ = ["n", "c", "d"]
    
        def __init__(self, n):
            self.n = n
            self.c = [0] * (n + 1)
            self.d = [0] * (n + 1)
    
        def update(self, x, v, cnt):
            while x <= self.n:
                if self.c[x] < v:
                    self.c[x] = v
                    self.d[x] = cnt
                elif self.c[x] == v:
                    self.d[x] += cnt
                x += x & -x
    
        def query(self, x):
            v = cnt = 0
            while x:
                if self.c[x] > v:
                    v = self.c[x]
                    cnt = self.d[x]
                elif self.c[x] == v:
                    cnt += self.d[x]
                x -= x & -x
            return v, cnt
    
    
    class Solution:
        def findNumberOfLIS(self, nums: List[int]) -> int:
            arr = sorted(set(nums))
            m = len(arr)
            tree = BinaryIndexedTree(m)
            for x in nums:
                i = bisect_left(arr, x) + 1
                v, cnt = tree.query(i - 1)
                tree.update(i, v + 1, max(cnt, 1))
            return tree.query(m)[1]
    
    
  • type BinaryIndexedTree struct {
    	n int
    	c []int
    	d []int
    }
    
    func newBinaryIndexedTree(n int) BinaryIndexedTree {
    	return BinaryIndexedTree{
    		n: n,
    		c: make([]int, n+1),
    		d: make([]int, n+1),
    	}
    }
    
    func (bit *BinaryIndexedTree) update(x, v, cnt int) {
    	for x <= bit.n {
    		if bit.c[x] < v {
    			bit.c[x] = v
    			bit.d[x] = cnt
    		} else if bit.c[x] == v {
    			bit.d[x] += cnt
    		}
    		x += x & -x
    	}
    }
    
    func (bit *BinaryIndexedTree) query(x int) (int, int) {
    	v, cnt := 0, 0
    	for x > 0 {
    		if bit.c[x] > v {
    			v = bit.c[x]
    			cnt = bit.d[x]
    		} else if bit.c[x] == v {
    			cnt += bit.d[x]
    		}
    		x -= x & -x
    	}
    	return v, cnt
    }
    
    func findNumberOfLIS(nums []int) int {
    	arr := make([]int, len(nums))
    	copy(arr, nums)
    	sort.Ints(arr)
    	m := len(arr)
    	tree := newBinaryIndexedTree(m)
    	for _, x := range nums {
    		i := sort.SearchInts(arr, x) + 1
    		v, cnt := tree.query(i - 1)
    		tree.update(i, v+1, max(cnt, 1))
    	}
    	_, ans := tree.query(m)
    	return ans
    }
    
  • class BinaryIndexedTree {
        private n: number;
        private c: number[];
        private d: number[];
    
        constructor(n: number) {
            this.n = n;
            this.c = Array(n + 1).fill(0);
            this.d = Array(n + 1).fill(0);
        }
    
        update(x: number, v: number, cnt: number): void {
            while (x <= this.n) {
                if (this.c[x] < v) {
                    this.c[x] = v;
                    this.d[x] = cnt;
                } else if (this.c[x] === v) {
                    this.d[x] += cnt;
                }
                x += x & -x;
            }
        }
    
        query(x: number): [number, number] {
            let v = 0;
            let cnt = 0;
            while (x > 0) {
                if (this.c[x] > v) {
                    v = this.c[x];
                    cnt = this.d[x];
                } else if (this.c[x] === v) {
                    cnt += this.d[x];
                }
                x -= x & -x;
            }
            return [v, cnt];
        }
    }
    
    function findNumberOfLIS(nums: number[]): number {
        const arr: number[] = [...new Set(nums)].sort((a, b) => a - b);
        const m: number = arr.length;
        const tree: BinaryIndexedTree = new BinaryIndexedTree(m);
        const search = (x: number): number => {
            let l = 0,
                r = arr.length;
            while (l < r) {
                const mid = (l + r) >> 1;
                if (arr[mid] >= x) {
                    r = mid;
                } else {
                    l = mid + 1;
                }
            }
            return l + 1;
        };
        for (const x of nums) {
            const i: number = search(x);
            const [v, cnt]: [number, number] = tree.query(i - 1);
            tree.update(i, v + 1, Math.max(cnt, 1));
        }
        return tree.query(m)[1];
    }
    
    
  • struct BinaryIndexedTree {
        n: usize,
        c: Vec<i32>,
        d: Vec<i32>,
    }
    
    impl BinaryIndexedTree {
        fn new(n: usize) -> BinaryIndexedTree {
            BinaryIndexedTree {
                n,
                c: vec![0; n + 1],
                d: vec![0; n + 1],
            }
        }
    
        fn update(&mut self, x: usize, v: i32, cnt: i32) {
            let mut x = x as usize;
            while x <= self.n {
                if self.c[x] < v {
                    self.c[x] = v;
                    self.d[x] = cnt;
                } else if self.c[x] == v {
                    self.d[x] += cnt;
                }
                x += x & x.wrapping_neg();
            }
        }
    
        fn query(&self, mut x: usize) -> (i32, i32) {
            let (mut v, mut cnt) = (0, 0);
            while x > 0 {
                if self.c[x] > v {
                    v = self.c[x];
                    cnt = self.d[x];
                } else if self.c[x] == v {
                    cnt += self.d[x];
                }
                x -= x & x.wrapping_neg();
            }
            (v, cnt)
        }
    }
    
    impl Solution {
        pub fn find_number_of_lis(nums: Vec<i32>) -> i32 {
            let mut arr: Vec<i32> = nums.iter().cloned().collect();
            arr.sort();
            let m = arr.len();
            let mut tree = BinaryIndexedTree::new(m);
            for x in nums.iter() {
                if let Ok(i) = arr.binary_search(x) {
                    let (v, cnt) = tree.query(i);
                    tree.update(i + 1, v + 1, cnt.max(1));
                }
            }
            let (_, ans) = tree.query(m);
            ans
        }
    }
    
    

All Problems

All Solutions