Open In App

Count ways to increase LCS length of two strings by one

Last Updated : 12 Mar, 2025
Summarize
Comments
Improve
Suggest changes
Share
Like Article
Like
Report

Given two strings consisting of lowercase characters, the task is to find the number of ways to insert a character in the first string such that the length of LCS of both strings increases by one. 

Examples:  

Input : s1 = “abab”, s2 = “abc”
Output : 3
Explanation: LCS length of given two strings is 2. There are 3 ways of insertion in s1 to increase the LCS length by one:
s1 = “abcab”, adding character 'c' at index 2.
s1 = “abacb”, adding character 'c' at index 3.
s1 = “ababc” adding character 'c at index 4.

Input: s1 = "abc", s2 = "abc"
Output: 0
Explanation: LCS length cannot be incremented.

Input : s1 = “abcabc”, s2 = “abcd”
Output : 4

[Naive Approach] Using Brute Force Method

The idea is to simulate the insertion of every possible lowercase character (a to z) at every possible position in S1 and compute the LCS of the modified S1 with S2. If the LCS length increases by one after the insertion, we count it as a valid way. This approach checks all possible combinations of insertions and directly verifies whether the LCS length increases.

Please note that we have directly used DP Solution of LCS Problem in the below code.

C++
// C++ program to get number of ways to increase
// LCS by 1
#include <bits/stdc++.h>
using namespace std;

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
int lcs(string &s1, string &s2) {
    int m = s1.size();
    int n = s2.size();

    // Initializing a matrix of size (m+1)*(n+1)
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

    // Building dp[m+1][n+1] in bottom-up fashion
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    // dp[m][n] contains length of LCS for s1[0..m-1]
    // and s2[0..n-1]
    return dp[m][n];
}

// Method returns total ways to increase LCS length by 1
int waysToIncreaseLCSBy1(string s1, string s2) {
    int n = s1.length(), m = s2.length();
    
    // Find the original LCS.
    int lcs1 = lcs(s1, s2);
    
    int ans = 0;
    
    // For all indices in s1. 
    for (int i=0; i<=n; i++) {
        
        // For characters in range [a, z].
        for (char ch='a'; ch<='z'; ch++) {
            
            // THis is the updated string s1.
            string updatedStr = s1.substr(0, i) + ch + s1.substr(i);
            
            // Find lcs
            int lcs2 = lcs(updatedStr, s2);
            
            // If new lcs = original lcs + 1.
            if (lcs2 == lcs1 + 1) ans++;
        }
    }
    
    return ans;
}

int main() {
    string s1 = "abab";
    string s2 = "abc";
    cout << waysToIncreaseLCSBy1(s1, s2);
    return 0;
}
Java
// Java program to get number of ways to increase
// LCS by 1

class GfG {

    // Returns length of LCS for s1[0..m-1], s2[0..n-1]
    static int lcs(String s1, String s2) {
        int m = s1.length();
        int n = s2.length();

        // Initializing a matrix of size (m+1)*(n+1)
        int[][] dp = new int[m + 1][n + 1];

        // Building dp[m+1][n+1] in bottom-up fashion
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1))
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }

        // dp[m][n] contains length of LCS for s1[0..m-1]
        // and s2[0..n-1]
        return dp[m][n];
    }

    // Method returns total ways to increase LCS length by 1
    static int waysToIncreaseLCSBy1(String s1, String s2) {
        int n = s1.length(), m = s2.length();

        // Find the original LCS.
        int lcs1 = lcs(s1, s2);

        int ans = 0;

        // For all indices in s1. 
        for (int i = 0; i <= n; i++) {

            // For characters in range [a, z].
            for (char ch = 'a'; ch <= 'z'; ch++) {

                // This is the updated string s1.
                String updatedStr = s1.substring(0, i) + ch + s1.substring(i);

                // Find lcs
                int lcs2 = lcs(updatedStr, s2);

                // If new lcs = original lcs + 1.
                if (lcs2 == lcs1 + 1) ans++;
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        String s1 = "abab";
        String s2 = "abc";
        System.out.println(waysToIncreaseLCSBy1(s1, s2));
    }
}
Python
# Python program to get number of ways to increase
# LCS by 1

# Returns length of LCS for s1[0..m-1], s2[0..n-1]
def lcs(s1, s2):
    m, n = len(s1), len(s2)

    # Initializing a matrix of size (m+1)*(n+1)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    # Building dp[m+1][n+1] in bottom-up fashion
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i - 1] == s2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    # dp[m][n] contains length of LCS for s1[0..m-1]
    # and s2[0..n-1]
    return dp[m][n]

# Method returns total ways to increase LCS length by 1
def waysToIncreaseLCSBy1(s1, s2):
    n, m = len(s1), len(s2)

    # Find the original LCS.
    lcs1 = lcs(s1, s2)

    ans = 0

    # For all indices in s1. 
    for i in range(n + 1):

        # For characters in range [a, z].
        for ch in range(ord('a'), ord('z') + 1):

            # This is the updated string s1.
            updatedStr = s1[:i] + chr(ch) + s1[i:]

            # Find lcs
            lcs2 = lcs(updatedStr, s2)

            # If new lcs = original lcs + 1.
            if lcs2 == lcs1 + 1:
                ans += 1

    return ans

if __name__ == "__main__":
    s1 = "abab"
    s2 = "abc"
    print(waysToIncreaseLCSBy1(s1, s2))
C#
// C# program to get number of ways to increase
// LCS by 1
using System;

class GfG {

    // Returns length of LCS for s1[0..m-1], s2[0..n-1]
    static int lcs(string s1, string s2) {
        int m = s1.Length;
        int n = s2.Length;

        // Initializing a matrix of size (m+1)*(n+1)
        int[,] dp = new int[m + 1, n + 1];

        // Building dp[m+1][n+1] in bottom-up fashion
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (s1[i - 1] == s2[j - 1])
                    dp[i, j] = dp[i - 1, j - 1] + 1;
                else
                    dp[i, j] = Math.Max(dp[i - 1, j], dp[i, j - 1]);
            }
        }

        // dp[m][n] contains length of LCS for s1[0..m-1]
        // and s2[0..n-1]
        return dp[m, n];
    }

    // Method returns total ways to increase LCS length by 1
    static int waysToIncreaseLCSBy1(string s1, string s2) {
        int n = s1.Length, m = s2.Length;

        // Find the original LCS.
        int lcs1 = lcs(s1, s2);

        int ans = 0;

        // For all indices in s1. 
        for (int i = 0; i <= n; i++) {

            // For characters in range [a, z].
            for (char ch = 'a'; ch <= 'z'; ch++) {

                // This is the updated string s1.
                string updatedStr = s1.Substring(0, i) + ch + s1.Substring(i);

                // Find lcs
                int lcs2 = lcs(updatedStr, s2);

                // If new lcs = original lcs + 1.
                if (lcs2 == lcs1 + 1) ans++;
            }
        }

        return ans;
    }

    static void Main() {
        string s1 = "abab";
        string s2 = "abc";
        Console.WriteLine(waysToIncreaseLCSBy1(s1, s2));
    }
}
JavaScript
// JavaScript program to get number of ways to increase
// LCS by 1

// Returns length of LCS for s1[0..m-1], s2[0..n-1]
function lcs(s1, s2) {
    let m = s1.length;
    let n = s2.length;

    // Initializing a matrix of size (m+1)*(n+1)
    let dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));

    // Building dp[m+1][n+1] in bottom-up fashion
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (s1[i - 1] === s2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    // dp[m][n] contains length of LCS for s1[0..m-1]
    // and s2[0..n-1]
    return dp[m][n];
}

// Method returns total ways to increase LCS length by 1
function waysToIncreaseLCSBy1(s1, s2) {
    let n = s1.length, m = s2.length;
    
    // Find the original LCS.
    let lcs1 = lcs(s1, s2);
    
    let ans = 0;
    
    // For all indices in s1. 
    for (let i = 0; i <= n; i++) {
        
        // For characters in range [a, z].
        for (let ch = 97; ch <= 122; ch++) {
            
            // This is the updated string s1.
            let updatedStr = s1.substring(0, i) + 
            String.fromCharCode(ch) + s1.substring(i);
            
            // Find lcs
            let lcs2 = lcs(updatedStr, s2);
            
            // If new lcs = original lcs + 1.
            if (lcs2 === lcs1 + 1) ans++;
        }
    }
    
    return ans;
}

let s1 = "abab";
let s2 = "abc";
console.log(waysToIncreaseLCSBy1(s1, s2));

Output
3

Time Complexity: O(n * 26 * (n + n*m)) which can be simplified into O(n^2 * m). For each index in the first string, we can add 26 characters. Concatenating substrings takes O(n) time and finding LCS take O(n*m) time.
Auxiliary Space: O(n * m) due to memo matrix.

[Efficient Approach] Using Prefix and Suffix DP

The idea is to use prefix and suffix dynamic programming (DP) tables to precompute the LCS lengths for all possible prefixes and suffixes of S1 and S2. By inserting a character into S1 at every possible position and checking if the combined LCS length from the prefix and suffix tables increases the overall LCS length by one, we can count the number of valid insertions.

In the previous approach, LCS of updated string S1 and string S2 was repeatedly calculated for all possible characters and indices. But in this approach, the LCS for all prefixes and suffixes of S1 and S2 are stored, therefore we can find the LCS of any prefix or suffix of S1 and S2 in O(1) time.

If by adding a character after i 'th index in string S1 and matching this character with the character at j 'th index in string S2, we will find the LCS of prefix of S1 ( from index 0 to i) and S2 (from index 0 to j-1) and LCS of suffix of S1 (from index i + 1) and S2 (from j + 1). If the sum of prefix and suffix LCS equals LCS of complete strings, it means LCS can be increased ( by including the matched character added after i 'th index).

The LCS length increases by one if:

  • lcsl[i][j-1] + lcsr[i+1][j+1] == lcsl[n][m], where:
    • lcsl[i][j-1] is the LCS length of the prefix S1[0..i-1] and S2[0..j-2].
    • lcsr[i+1][j+1] is the LCS length of the suffix S1[i..n-1] and S2[j..m-1].
    • lcsl[n][m] is the LCS length of the entire strings S1 and S2.

Step by step approach:

  1. Compute the prefix LCS table (lcsl) for all prefixes of S1 and S2.
  2. Compute the suffix LCS table (lcsr) for all suffixes of S1 and S2.
  3. Iterate over all possible insertion positions in S1.
  4. For each insertion position, try inserting every lowercase character (a to z).
  5. For each character, check if inserting it at the current position increases the LCS length by one using the prefix and suffix LCS tables. If the condition is satisfied, increment the answer.
  6. Return the total count of valid insertions.

Note: The break statement is important because it ensures that we count each valid insertion only once for a given position i in S1. Without the break, we would overcount by considering multiple valid j positions in S2 for the same character c at position i, which would incorrectly increment the result. The goal is to count the number of ways (characters or indices) in S1, not different combinations of i and j.

C++
// C++ program to get number of ways to increase
// LCS by 1
#include <bits/stdc++.h>
using namespace std;

// Method returns total ways to increase LCS length by 1
int waysToIncreaseLCSBy1(string s1, string s2) {
    int n = s1.length(), m = s2.length();
    
    vector<vector<int>> lcsl(n+2, vector<int>(m+2, 0));
    vector<vector<int>> lcsr(n+2, vector<int>(m+2, 0));

    // Filling LCS array for prefix substrings
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (s1[i-1] == s2[j-1])
                lcsl[i][j] = 1 + lcsl[i-1][j-1];
            else
                lcsl[i][j] = max(lcsl[i-1][j],
                                lcsl[i][j-1]);
        }
    }

    // Filling LCS array for suffix substrings
    for (int i = n; i >= 1; i--) {
        for (int j = m; j >= 1; j--) {
            if (s1[i-1] == s2[j-1])
                lcsr[i][j] = 1 + lcsr[i+1][j+1];
            else
                lcsr[i][j] = max(lcsr[i+1][j],
                                 lcsr[i][j+1]);
        }
    }

    // Looping for all possible insertion positions
    // in first string
    int ans = 0;
    for (int i=0; i<=n; i++) {
        
        // Trying all possible lower case characters
        for (char c='a'; c<='z'; c++) {
            
            // Find all positions of char c 
            // in string s2.
            for (int j=1; j<=m; j++) {
                if (s2[j-1] == c) {
                    
                    // If adding char c at position (i+1) increases 
                    // LCS, increment ans and break.
                    if (lcsl[i][j-1] + lcsr[i+1][j+1] == lcsl[n][m]) {
                        ans++;
                        break;
                    } 
                }
            }
        }
    }

    return ans;
}

int main() {
    string s1 = "abab";
    string s2 = "abc";
    cout << waysToIncreaseLCSBy1(s1, s2);
    return 0;
}
Java
// Java program to get number of ways to increase
// LCS by 1
import java.util.*;

class GfG {
    
    // Method returns total ways to increase LCS length by 1
    static int waysToIncreaseLCSBy1(String s1, String s2) {
        int n = s1.length(), m = s2.length();
        
        int[][] lcsl = new int[n+2][m+2];
        int[][] lcsr = new int[n+2][m+2];

        // Filling LCS array for prefix substrings
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (s1.charAt(i-1) == s2.charAt(j-1))
                    lcsl[i][j] = 1 + lcsl[i-1][j-1];
                else
                    lcsl[i][j] = Math.max(lcsl[i-1][j], lcsl[i][j-1]);
            }
        }

        // Filling LCS array for suffix substrings
        for (int i = n; i >= 1; i--) {
            for (int j = m; j >= 1; j--) {
                if (s1.charAt(i-1) == s2.charAt(j-1))
                    lcsr[i][j] = 1 + lcsr[i+1][j+1];
                else
                    lcsr[i][j] = Math.max(lcsr[i+1][j], lcsr[i][j+1]);
            }
        }

        // Looping for all possible insertion positions
        // in first string
        int ans = 0;
        for (int i = 0; i <= n; i++) {
            
            // Trying all possible lower case characters
            for (char c = 'a'; c <= 'z'; c++) {
                
                // Find all positions of char c 
                // in string s2.
                for (int j = 1; j <= m; j++) {
                    if (s2.charAt(j-1) == c) {
                        
                        // If adding char c at position (i+1) increases 
                        // LCS, increment ans and break.
                        if (lcsl[i][j-1] + lcsr[i+1][j+1] == lcsl[n][m]) {
                            ans++;
                            break;
                        } 
                    }
                }
            }
        }

        return ans;
    }

    public static void main(String[] args) {
        String s1 = "abab";
        String s2 = "abc";
        System.out.println(waysToIncreaseLCSBy1(s1, s2));
    }
}
Python
# Python program to get number of ways to increase
# LCS by 1

# Method returns total ways to increase LCS length by 1
def waysToIncreaseLCSBy1(s1, s2):
    n, m = len(s1), len(s2)
    
    lcsl = [[0] * (m + 2) for _ in range(n + 2)]
    lcsr = [[0] * (m + 2) for _ in range(n + 2)]

    # Filling LCS array for prefix substrings
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if s1[i - 1] == s2[j - 1]:
                lcsl[i][j] = 1 + lcsl[i - 1][j - 1]
            else:
                lcsl[i][j] = max(lcsl[i - 1][j], lcsl[i][j - 1])

    # Filling LCS array for suffix substrings
    for i in range(n, 0, -1):
        for j in range(m, 0, -1):
            if s1[i - 1] == s2[j - 1]:
                lcsr[i][j] = 1 + lcsr[i + 1][j + 1]
            else:
                lcsr[i][j] = max(lcsr[i + 1][j], lcsr[i][j + 1])

    # Looping for all possible insertion positions
    # in first string
    ans = 0
    for i in range(n + 1):
        
        # Trying all possible lower case characters
        for c in range(ord('a'), ord('z') + 1):
            
            # Find all positions of char c 
            # in string s2.
            for j in range(1, m + 1):
                if s2[j - 1] == chr(c):
                    
                    # If adding char c at position (i+1) increases 
                    # LCS, increment ans and break.
                    if lcsl[i][j - 1] + lcsr[i + 1][j + 1] == lcsl[n][m]:
                        ans += 1
                        break

    return ans

if __name__ == "__main__":
    s1 = "abab"
    s2 = "abc"
    print(waysToIncreaseLCSBy1(s1, s2))
C#
// C# program to get number of ways to increase
// LCS by 1
using System;

class GfG {

    // Method returns total ways to increase LCS length by 1
    static int waysToIncreaseLCSBy1(string s1, string s2) {
        int n = s1.Length, m = s2.Length;
        
        int[,] lcsl = new int[n+2, m+2];
        int[,] lcsr = new int[n+2, m+2];

        // Filling LCS array for prefix substrings
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (s1[i-1] == s2[j-1])
                    lcsl[i, j] = 1 + lcsl[i-1, j-1];
                else
                    lcsl[i, j] = Math.Max(lcsl[i-1, j], lcsl[i, j-1]);
            }
        }

        // Filling LCS array for suffix substrings
        for (int i = n; i >= 1; i--) {
            for (int j = m; j >= 1; j--) {
                if (s1[i-1] == s2[j-1])
                    lcsr[i, j] = 1 + lcsr[i+1, j+1];
                else
                    lcsr[i, j] = Math.Max(lcsr[i+1, j], lcsr[i, j+1]);
            }
        }

        // Looping for all possible insertion positions
        // in first string
        int ans = 0;
        for (int i = 0; i <= n; i++) {
            
            // Trying all possible lower case characters
            for (char c = 'a'; c <= 'z'; c++) {
                
                // Find all positions of char c 
                // in string s2.
                for (int j = 1; j <= m; j++) {
                    if (s2[j-1] == c) {
                        
                        // If adding char c at position (i+1) increases 
                        // LCS, increment ans and break.
                        if (lcsl[i, j-1] + lcsr[i+1, j+1] == lcsl[n, m]) {
                            ans++;
                            break;
                        } 
                    }
                }
            }
        }

        return ans;
    }

    static void Main() {
        string s1 = "abab";
        string s2 = "abc";
        Console.WriteLine(waysToIncreaseLCSBy1(s1, s2));
    }
}
JavaScript
// JavaScript program to get number of ways to increase
// LCS by 1

// Method returns total ways to increase LCS length by 1
function waysToIncreaseLCSBy1(s1, s2) {
    let n = s1.length, m = s2.length;
    
    let lcsl = Array.from({ length: n + 2 }, () => Array(m + 2).fill(0));
    let lcsr = Array.from({ length: n + 2 }, () => Array(m + 2).fill(0));

    // Filling LCS array for prefix substrings
    for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= m; j++) {
            if (s1[i - 1] === s2[j - 1])
                lcsl[i][j] = 1 + lcsl[i - 1][j - 1];
            else
                lcsl[i][j] = Math.max(lcsl[i - 1][j], lcsl[i][j - 1]);
        }
    }

    // Filling LCS array for suffix substrings
    for (let i = n; i >= 1; i--) {
        for (let j = m; j >= 1; j--) {
            if (s1[i - 1] === s2[j - 1])
                lcsr[i][j] = 1 + lcsr[i + 1][j + 1];
            else
                lcsr[i][j] = Math.max(lcsr[i + 1][j], lcsr[i][j + 1]);
        }
    }

    // Looping for all possible insertion positions
    // in first string
    let ans = 0;
    for (let i = 0; i <= n; i++) {
        
        // Trying all possible lower case characters
        for (let c = 'a'.charCodeAt(0); c <= 'z'.charCodeAt(0); c++) {
            let charC = String.fromCharCode(c);

            // Find all positions of char c 
            // in string s2.
            for (let j = 1; j <= m; j++) {
                if (s2[j - 1] === charC) {
                    
                    // If adding char c at position (i+1) increases 
                    // LCS, increment ans and break.
                    if (lcsl[i][j - 1] + lcsr[i + 1][j + 1] === lcsl[n][m]) {
                        ans++;
                        break;
                    } 
                }
            }
        }
    }

    return ans;
}

let s1 = "abab";
let s2 = "abc";
console.log(waysToIncreaseLCSBy1(s1, s2));

Output
3

Time Complexity : O(n * m), where n is the length of string s1 and m is the length of string s2. 
Auxiliary Space : O(n * m)


Next Article

Similar Reads