šŸ“ Longest Increasing Subsequence (LIS) – Complete In-Depth Explanation

The **Longest Increasing Subsequence (LIS)** problem is a **dynamic programming problem** that finds the longest subsequence of a given sequence where the elements are **in increasing order** (not necessarily contiguous).

šŸ“Œ Why is LIS Important?

  • āœ… **Used in stock market analysis for trend prediction.**
  • šŸš€ **Helps in finding the longest ordered sequence in data.**
  • šŸ” **Useful in bioinformatics for DNA sequencing.**
  • šŸ“” **Applied in computer vision for motion analysis.**

šŸ“Œ Understanding LIS with an Example

Given an array:


                    arr = [10, 22, 9, 33, 21, 50, 41, 60]
                

The **Longest Increasing Subsequence (LIS)** is **[10, 22, 33, 50, 60]** (length = 5).

šŸ”¹ **Key Points**

  • āœ… The elements **must be in increasing order** but **not necessarily contiguous**.
  • šŸ“‰ The LIS **may not be unique**; multiple valid solutions may exist.

šŸ›  Solution Approaches

šŸ“Œ **1. Recursion (Brute Force) – O(2^N) Complexity**

A **recursive solution** checks whether to include an element or exclude it.


                int LIS_Rec(vector& arr, int prev, int index) {
                    if (index == arr.size()) return 0;
            
                    int exclude = LIS_Rec(arr, prev, index + 1);
                    int include = 0;
                    if (prev == -1 || arr[index] > arr[prev]) 
                        include = 1 + LIS_Rec(arr, index, index + 1);
            
                    return max(include, exclude);
                }
                

šŸ”“ **Disadvantage:** This solution is **exponential (O(2^N))** and inefficient for large inputs.

šŸ“Œ **2. Dynamic Programming (O(N²) Complexity)**

Instead of recomputing results, we store them in a **DP array**.


                int LIS_DP(vector& arr) {
                    int N = arr.size();
                    vector dp(N, 1);
            
                    for (int i = 1; i < N; i++) {
                        for (int j = 0; j < i; j++) {
                            if (arr[i] > arr[j]) 
                                dp[i] = max(dp[i], dp[j] + 1);
                        }
                    }
                    return *max_element(dp.begin(), dp.end());
                }
                

šŸ“Œ **3. Optimized LIS Using Binary Search (O(N log N))**

We maintain a **sorted array** where each element **replaces** the first larger element found using binary search.


                int LIS_BinarySearch(vector& arr) {
                    vector lis;
                    for (int num : arr) {
                        auto it = lower_bound(lis.begin(), lis.end(), num);
                        if (it == lis.end()) lis.push_back(num);
                        else *it = num;
                    }
                    return lis.size();
                }
                

šŸ“Œ Printing the LIS Sequence

We store the indices of predecessors to reconstruct the LIS.


                vector printLIS(vector& arr) {
                    int N = arr.size();
                    vector dp(N, 1), parent(N, -1);
                    int maxLen = 0, endIdx = 0;
            
                    for (int i = 1; i < N; i++) {
                        for (int j = 0; j < i; j++) {
                            if (arr[i] > arr[j] && dp[i] < dp[j] + 1) {
                                dp[i] = dp[j] + 1;
                                parent[i] = j;
                            }
                        }
                        if (dp[i] > maxLen) {
                            maxLen = dp[i];
                            endIdx = i;
                        }
                    }
            
                    vector lis;
                    for (int i = endIdx; i >= 0; i = parent[i]) {
                        lis.push_back(arr[i]);
                        if (parent[i] == -1) break;
                    }
                    reverse(lis.begin(), lis.end());
                    return lis;
                }
                

ā³ Time Complexity Analysis

Algorithm Time Complexity
Recursive (Brute Force) O(2^N)
Dynamic Programming (DP Table) O(N²)
Binary Search Optimization O(N log N)

šŸŒ Real-World Applications of LIS

  • šŸ“ˆ **Stock Market Analysis** – Finding upward trends.
  • 🧬 **Bioinformatics** – DNA sequence alignment.
  • šŸ“” **Data Compression** – Identifying ordered sequences.
  • šŸ”— **Pattern Recognition** – Analyzing ordered patterns.

šŸ”— Next Topics