By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
"This pattern appears in 1 out of every 3 onsite interviews—nail it and you prove you can optimize dynamic programming problems under pressure."
Before tackling LIS, ensure you understand: 1. Dynamic Programming (DP) – Memoization, tabulation, and state transitions. 2. Binary Search – Efficiently finding insertion points in sorted arrays. 3. Time Complexity Analysis – Recognizing O(n²) vs. O(n log n) trade-offs.
dp[i] = max(dp[j] + 1) for j < i where nums[j] < nums[i]
tails
tails[i]
i+1
dp
bisect_left
≤
<
Run this every time you see an LIS problem:
nums[i] < nums[j]
nums[i] ≤ nums[j]
Are there constraints (e.g., no duplicates, circular array)?
Choose the Right Approach
Follow-up (return sequence)? → Backtrack with parent pointers
Implement DP (If Applicable)
dp = [1] n
i
1
n-1
j
0
i-1
nums[j] < nums[i]
dp[i] = max(dp[i], dp[j] + 1)
Return max(dp).
max(dp)
Implement Greedy + Binary Search (If Applicable)
tails = []
num
nums
tails[i] ≥ num
len(tails)
tails[index] = num
Return len(tails).
Handle Follow-Ups
nums[1:] + nums[:1]
Non-strictly increasing? → Adjust comparisons (≤ instead of <).
Optimize Space (If Needed)
Greedy: tails is already O(n).
Test Edge Cases
n
Problem: Given an integer array nums, return the length of the longest strictly increasing subsequence.
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18] Output: 4 (LIS: [2, 3, 7, 101])
nums = [10, 9, 2, 5, 3, 7, 101, 18]
4
[2, 3, 7, 101]
dp = [1] 8
i=1
nums[1]=9
nums[j] < 9
dp[1]=1
i=2
nums[2]=2
j=0
nums[0]=10
j=1
dp[2]=1
i=3
nums[3]=5
j=2
dp[3] = max(1, dp[2]+1) = 2
i=4
nums[4]=3
dp[4] = max(1, dp[2]+1) = 2
i=5
nums[5]=7
j=3
j=4
dp[5] = max(1, dp[2]+1, dp[3]+1, dp[4]+1) = 3
i=6
nums[6]=101
j=5
dp[6] = 4
i=7
nums[7]=18
dp[7] = 4
max(dp) = 4
def lengthOfLIS(nums): if not nums: return 0 dp = [1] len(nums) for i in range(1, len(nums)): for j in range(i): if nums[j] < nums[i]: dp[i] = max(dp[i], dp[j] + 1) return max(dp)
Time Complexity: O(n²) Space Complexity: O(n)
Why This Works: - dp[i] stores the length of the LIS ending at nums[i]. - For each i, we check all previous j to see if nums[j] can extend the subsequence.
dp[i]
nums[i]
nums[j]
Problem: Same as above, but nums can be large (n ≤ 10⁵).
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18] Output: 4
num=10
tails = [10]
num=9
tails[0]
tails = [9]
num=2
tails = [2]
num=5
tails = [2, 5]
num=3
tails[1]
tails = [2, 3]
num=7
tails = [2, 3, 7]
num=101
tails = [2, 3, 7, 101]
num=18
tails[3]
tails = [2, 3, 7, 18]
len(tails) = 4
import bisect def lengthOfLIS(nums): tails = [] for num in nums: idx = bisect.bisect_left(tails, num) if idx == len(tails): tails.append(num) else: tails[idx] = num return len(tails)
Time Complexity: O(n log n) Space Complexity: O(n)
Why This Works: - tails maintains the smallest possible tail for each LIS length. - Binary search (bisect_left) finds where to place num in O(log n) time.
Problem: Return the longest increasing subsequence itself (not just length).
Input: nums = [10, 9, 2, 5, 3, 7, 101, 18] Output: [2, 3, 7, 101] (or [2, 5, 7, 101], etc.)
[2, 5, 7, 101]
parent
def lengthOfLIS(nums): if not nums: return [] dp = [1] len(nums) parent = [-1] len(nums) for i in range(1, len(nums)): for j in range(i): if nums[j] < nums[i] and dp[j] + 1 > dp[i]: dp[i] = dp[j] + 1 parent[i] = j max_len = max(dp) max_idx = dp.index(max_len) res = [] while max_idx != -1: res.append(nums[max_idx]) max_idx = parent[max_idx] return res[::-1]
Why This Works: - parent[i] stores the index of the previous element in the LIS ending at i. - Backtracking from max_idx reconstructs the sequence.
parent[i]
max_idx
bisect_right
nums = []
nums = [5]
[5, 4, 3, 2, 1]
"Alright, let’s lock this in. For LIS problems, start by clarifying: strict or non-strict? Length or sequence? Small or large input? If it’s small (n ≤ 10³), use DP—initialize dp = [1] n, fill it with nested loops, and return max(dp). For large inputs, switch to greedy + binary search: maintain a tails array, use bisect_left to find insertion points, and return len(tails). If they ask for the sequence, track parent pointers during DP or backtrack from tails. Watch out for edge cases—empty array, single element, duplicates—and adjust comparisons for non-strict LIS. Practice both approaches, and you’ll crush this in any interview. Good luck!
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.