Welcome to Subscribe On Youtube

2407. Longest Increasing Subsequence II

Description

You are given an integer array nums and an integer k.

Find the longest subsequence of nums that meets the following requirements:

  • The subsequence is strictly increasing and
  • The difference between adjacent elements in the subsequence is at most k.

Return the length of the longest subsequence that meets the requirements.

A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements.

 

Example 1:

Input: nums = [4,2,1,4,3,4,5,8,15], k = 3
Output: 5
Explanation:
The longest subsequence that meets the requirements is [1,3,4,5,8].
The subsequence has a length of 5, so we return 5.
Note that the subsequence [1,3,4,5,8,15] does not meet the requirements because 15 - 8 = 7 is larger than 3.

Example 2:

Input: nums = [7,4,5,1,8,12,4,7], k = 5
Output: 4
Explanation:
The longest subsequence that meets the requirements is [4,5,8,12].
The subsequence has a length of 4, so we return 4.

Example 3:

Input: nums = [1,5], k = 1
Output: 1
Explanation:
The longest subsequence that meets the requirements is [1].
The subsequence has a length of 1, so we return 1.

 

Constraints:

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

Solutions

Solution 1: Segment Tree

We assume that $f[v]$ represents the length of the longest increasing subsequence ending with the number $v$.

We traverse each element $v$ in the array $nums$, with the state transition equation: $f[v] = \max(f[v], f[x])$, where the range of $x$ is $[v-k, v-1]$.

Therefore, we need a data structure to maintain the maximum value of the interval. It is not difficult to think of using a segment tree.

The segment tree divides the entire interval into multiple discontinuous subintervals, and the number of subintervals does not exceed $log(width)$. To update the value of an element, only $log(width)$ intervals need to be updated, and these intervals are all contained in a large interval that contains the element.

  • Each node of the segment tree represents an interval;
  • The segment tree has a unique root node, which represents the entire statistical range, such as $[1,N]$;
  • Each leaf node of the segment tree represents an elementary interval of length $1$, $[x, x]$;
  • For each internal node $[l,r]$, its left child is $[l,mid]$, and the right child is $[mid+1,r]$, where $mid = \left \lfloor \frac{l+r}{2} \right \rfloor$.

For this problem, the information maintained by the segment tree node is the maximum value within the interval range.

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

  • class Solution {
        public int lengthOfLIS(int[] nums, int k) {
            int mx = nums[0];
            for (int v : nums) {
                mx = Math.max(mx, v);
            }
            SegmentTree tree = new SegmentTree(mx);
            int ans = 0;
            for (int v : nums) {
                int t = tree.query(1, v - k, v - 1) + 1;
                ans = Math.max(ans, t);
                tree.modify(1, v, t);
            }
            return ans;
        }
    }
    
    class Node {
        int l;
        int r;
        int v;
    }
    
    class SegmentTree {
        private Node[] tr;
    
        public SegmentTree(int n) {
            tr = new Node[4 * n];
            for (int i = 0; i < tr.length; ++i) {
                tr[i] = new Node();
            }
            build(1, 1, n);
        }
    
        public void build(int u, int l, int r) {
            tr[u].l = l;
            tr[u].r = r;
            if (l == r) {
                return;
            }
            int mid = (l + r) >> 1;
            build(u << 1, l, mid);
            build(u << 1 | 1, mid + 1, r);
        }
    
        public void modify(int u, int x, int v) {
            if (tr[u].l == x && tr[u].r == x) {
                tr[u].v = v;
                return;
            }
            int mid = (tr[u].l + tr[u].r) >> 1;
            if (x <= mid) {
                modify(u << 1, x, v);
            } else {
                modify(u << 1 | 1, x, v);
            }
            pushup(u);
        }
    
        public void pushup(int u) {
            tr[u].v = Math.max(tr[u << 1].v, tr[u << 1 | 1].v);
        }
    
        public int query(int u, int l, int r) {
            if (tr[u].l >= l && tr[u].r <= r) {
                return tr[u].v;
            }
            int mid = (tr[u].l + tr[u].r) >> 1;
            int v = 0;
            if (l <= mid) {
                v = query(u << 1, l, r);
            }
            if (r > mid) {
                v = Math.max(v, query(u << 1 | 1, l, r));
            }
            return v;
        }
    }
    
  • class Node {
    public:
        int l;
        int r;
        int v;
    };
    
    class SegmentTree {
    public:
        vector<Node*> tr;
    
        SegmentTree(int n) {
            tr.resize(4 * n);
            for (int i = 0; i < tr.size(); ++i) tr[i] = new Node();
            build(1, 1, n);
        }
    
        void build(int u, int l, int r) {
            tr[u]->l = l;
            tr[u]->r = r;
            if (l == r) return;
            int mid = (l + r) >> 1;
            build(u << 1, l, mid);
            build(u << 1 | 1, mid + 1, r);
        }
    
        void modify(int u, int x, int v) {
            if (tr[u]->l == x && tr[u]->r == x) {
                tr[u]->v = v;
                return;
            }
            int mid = (tr[u]->l + tr[u]->r) >> 1;
            if (x <= mid)
                modify(u << 1, x, v);
            else
                modify(u << 1 | 1, x, v);
            pushup(u);
        }
    
        void pushup(int u) {
            tr[u]->v = max(tr[u << 1]->v, tr[u << 1 | 1]->v);
        }
    
        int query(int u, int l, int r) {
            if (tr[u]->l >= l && tr[u]->r <= r) return tr[u]->v;
            int mid = (tr[u]->l + tr[u]->r) >> 1;
            int v = 0;
            if (l <= mid) v = query(u << 1, l, r);
            if (r > mid) v = max(v, query(u << 1 | 1, l, r));
            return v;
        }
    };
    
    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums, int k) {
            SegmentTree* tree = new SegmentTree(*max_element(nums.begin(), nums.end()));
            int ans = 1;
            for (int v : nums) {
                int t = tree->query(1, v - k, v - 1) + 1;
                ans = max(ans, t);
                tree->modify(1, v, t);
            }
            return ans;
        }
    };
    
  • class Node:
        def __init__(self):
            self.l = 0
            self.r = 0
            self.v = 0
    
    
    class SegmentTree:
        def __init__(self, n):
            self.tr = [Node() for _ in range(4 * n)]
            self.build(1, 1, n)
    
        def build(self, u, l, r):
            self.tr[u].l = l
            self.tr[u].r = r
            if l == r:
                return
            mid = (l + r) >> 1
            self.build(u << 1, l, mid)
            self.build(u << 1 | 1, mid + 1, r)
    
        def modify(self, u, x, v):
            if self.tr[u].l == x and self.tr[u].r == x:
                self.tr[u].v = v
                return
            mid = (self.tr[u].l + self.tr[u].r) >> 1
            if x <= mid:
                self.modify(u << 1, x, v)
            else:
                self.modify(u << 1 | 1, x, v)
            self.pushup(u)
    
        def pushup(self, u):
            self.tr[u].v = max(self.tr[u << 1].v, self.tr[u << 1 | 1].v)
    
        def query(self, u, l, r):
            if self.tr[u].l >= l and self.tr[u].r <= r:
                return self.tr[u].v
            mid = (self.tr[u].l + self.tr[u].r) >> 1
            v = 0
            if l <= mid:
                v = self.query(u << 1, l, r)
            if r > mid:
                v = max(v, self.query(u << 1 | 1, l, r))
            return v
    
    
    class Solution:
        def lengthOfLIS(self, nums: List[int], k: int) -> int:
            tree = SegmentTree(max(nums))
            ans = 1
            for v in nums:
                t = tree.query(1, v - k, v - 1) + 1
                ans = max(ans, t)
                tree.modify(1, v, t)
            return ans
    
    
  • func lengthOfLIS(nums []int, k int) int {
    	mx := slices.Max(nums)
    	tree := newSegmentTree(mx)
    	ans := 1
    	for _, v := range nums {
    		t := tree.query(1, v-k, v-1) + 1
    		ans = max(ans, t)
    		tree.modify(1, v, t)
    	}
    	return ans
    }
    
    type node struct {
    	l int
    	r int
    	v int
    }
    
    type segmentTree struct {
    	tr []*node
    }
    
    func newSegmentTree(n int) *segmentTree {
    	tr := make([]*node, n<<2)
    	for i := range tr {
    		tr[i] = &node{}
    	}
    	t := &segmentTree{tr}
    	t.build(1, 1, n)
    	return t
    }
    
    func (t *segmentTree) build(u, l, r int) {
    	t.tr[u].l, t.tr[u].r = l, r
    	if l == r {
    		return
    	}
    	mid := (l + r) >> 1
    	t.build(u<<1, l, mid)
    	t.build(u<<1|1, mid+1, r)
    	t.pushup(u)
    }
    
    func (t *segmentTree) modify(u, x, v int) {
    	if t.tr[u].l == x && t.tr[u].r == x {
    		t.tr[u].v = v
    		return
    	}
    	mid := (t.tr[u].l + t.tr[u].r) >> 1
    	if x <= mid {
    		t.modify(u<<1, x, v)
    	} else {
    		t.modify(u<<1|1, x, v)
    	}
    	t.pushup(u)
    }
    
    func (t *segmentTree) query(u, l, r int) int {
    	if t.tr[u].l >= l && t.tr[u].r <= r {
    		return t.tr[u].v
    	}
    	mid := (t.tr[u].l + t.tr[u].r) >> 1
    	v := 0
    	if l <= mid {
    		v = t.query(u<<1, l, r)
    	}
    	if r > mid {
    		v = max(v, t.query(u<<1|1, l, r))
    	}
    	return v
    }
    
    func (t *segmentTree) pushup(u int) {
    	t.tr[u].v = max(t.tr[u<<1].v, t.tr[u<<1|1].v)
    }
    

All Problems

All Solutions