Maximum Matching Pairs

Last Updated : 31 Jul, 2026

Given two strings s1 and s2 of lengths n and m, respectively, consisting only of the characters '*', '#', and '@', find the maximum number of matching character pairs that can be formed such that:

  • Each character can be paired at most once.
  • A pair can only be formed between equal characters.
  • If s1[i] is paired with s2[j] and s1[k] is paired with s2[l], where i < k, then j < l.

Return the maximum number of pairs that can be formed.

Examples:

Input: s1 = "*@#*", s2 = "*#"
Output: 2
Explanation: The first '*' in s1 can be paired with '*' in s2, and '#' in s1 can be paired with '#' in s2. These pairs preserve the relative order in both strings, so the maximum number of matching pairs is 2.

Input: s1 = "***", s2 = "##"
Output: 0
Explanation: The two strings do not contain any common characters, so no matching pairs can be formed.

Try It Yourself
redirect icon

[Naive Approach] Using Recursion - O(2 ^ (n + m)) Time and O(n + m) Space

The order of paired characters must remain the same in both strings, making this problem equivalent to finding the Longest Common Subsequence (LCS). Thus, the idea is to start from the last characters of both strings, either pair them if they match or skip one character and recursively choose the option that forms the maximum number of pairs.

  • Start recursion from the last characters of both strings.
  • If either string becomes empty, return 0 as no more pairs can be formed.
  • If the current characters are equal, pair them and recursively solve for the remaining prefixes.
  • Otherwise, recursively skip one character from either the first or the second string.
  • Return the maximum value obtained from the two recursive calls.
C++
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;

int solve(string &s1, string &s2, int i, int j)
{
    // Base case: If either string becomes empty,
    // no more pairs can be formed.
    if (i == 0 || j == 0)
        return 0;

    // If the current characters match,
    // pair them and move to the remaining prefixes.
    if (s1[i - 1] == s2[j - 1])
        return 1 + solve(s1, s2, i - 1, j - 1);

    // Otherwise, skip one character from either string
    // and return the maximum result.
    return max(solve(s1, s2, i - 1, j), solve(s1, s2, i, j - 1));
}

int maxMatchingPairs(string &s1, string &s2)
{
    int n = s1.size();
    int m = s2.size();

    // Start recursion from the complete strings.
    return solve(s1, s2, n, m);
}

int main()
{
    string s1 = "*@#";
    string s2 = "*#";

    cout << maxMatchingPairs(s1, s2) << endl;

    return 0;
}
Java
public class GFG {
    static int solve(String s1, String s2, int i, int j)
    {
        // Base case: If either string becomes empty,
        // no more pairs can be formed.
        if (i == 0 || j == 0)
            return 0;

        // If the current characters match,
        // pair them and move to the remaining prefixes.
        if (s1.charAt(i - 1) == s2.charAt(j - 1))
            return 1 + solve(s1, s2, i - 1, j - 1);

        // Otherwise, skip one character from either string
        // and return the maximum result.
        return Math.max(solve(s1, s2, i - 1, j),
                        solve(s1, s2, i, j - 1));
    }

    static int maxMatchingPairs(String s1, String s2)
    {
        int n = s1.length();
        int m = s2.length();

        // Start recursion from the complete strings.
        return solve(s1, s2, n, m);
    }

    public static void main(String[] args)
    {
        String s1 = "*@#";
        String s2 = "*#";

        System.out.println(maxMatchingPairs(s1, s2));
    }
}
Python
def solve(s1, s2, i, j):

    # Base case: If either string becomes empty,
    # no more pairs can be formed.
    if i == 0 or j == 0:
        return 0

    # If the current characters match,
    # pair them and move to the remaining prefixes.
    if s1[i - 1] == s2[j - 1]:
        return 1 + solve(s1, s2, i - 1, j - 1)

    # Otherwise, skip one character from either string
    # and return the maximum result.
    return max(solve(s1, s2, i - 1, j),
               solve(s1, s2, i, j - 1))


def maxMatchingPairs(s1, s2):
    n = len(s1)
    m = len(s2)

    # Start recursion from the complete strings.
    return solve(s1, s2, n, m)

# Driver Code
if __name__ == "__main__":
    s1 = "*@#"
    s2 = "*#"

    print(maxMatchingPairs(s1, s2))
C#
using System;

class GFG {
    static int Solve(string s1, string s2, int i, int j)
    {
        // Base case: If either string becomes empty,
        // no more pairs can be formed.
        if (i == 0 || j == 0)
            return 0;

        // If the current characters match,
        // pair them and move to the remaining prefixes.
        if (s1[i - 1] == s2[j - 1])
            return 1 + Solve(s1, s2, i - 1, j - 1);

        // Otherwise, skip one character from either string
        // and return the maximum result.
        return Math.Max(Solve(s1, s2, i - 1, j),
                        Solve(s1, s2, i, j - 1));
    }

    static int maxMatchingPairs(string s1, string s2)
    {
        int n = s1.Length;
        int m = s2.Length;

        // Start recursion from the complete strings.
        return Solve(s1, s2, n, m);
    }

    static void Main()
    {
        string s1 = "*@#";
        string s2 = "*#";

        Console.WriteLine(maxMatchingPairs(s1, s2));
    }
}
JavaScript
function solve(s1, s2, i, j)
{
    // Base case: If either string becomes empty,
    // no more pairs can be formed.
    if (i === 0 || j === 0)
        return 0;

    // If the current characters match,
    // pair them and move to the remaining prefixes.
    if (s1[i - 1] === s2[j - 1])
        return 1 + solve(s1, s2, i - 1, j - 1);

    // Otherwise, skip one character from either string
    // and return the maximum result.
    return Math.max(solve(s1, s2, i - 1, j),
                    solve(s1, s2, i, j - 1));
}

function maxMatchingPairs(s1, s2)
{
    const n = s1.length;
    const m = s2.length;

    // Start recursion from the complete strings.
    return solve(s1, s2, n, m);
}

// Driver code
const s1 = "*@#";
const s2 = "*#";

console.log(maxMatchingPairs(s1, s2));

Output
2

[Better Approach] Using Memoization(DP) - O(n * m) Time and O(n * m) Space

The recursive solution repeatedly solves the same pair of indices (i, j), leading to many redundant computations. To avoid this, use Memoization(Dynamic Programming). The idea is to store the result of each state in a DP table. Whenever the same state is encountered again, simply return the stored value instead of recomputing it.

  • Create a 2D DP table of size (n + 1) × (m + 1) and initialize all entries with -1.
  • Start recursion from the last characters of both strings.
  • If either string becomes empty, return 0.
  • Before processing a state (i, j), check if its value is already stored in the DP table. If yes, return the stored value.
  • If the current characters are equal, pair them and store 1 + solve(i - 1, j - 1) in the DP table.
  • Otherwise, store the maximum of solve(i - 1, j) and solve(i, j - 1) in the DP table, and return it.
C++
#include <bits/stdc++.h>
using namespace std;

int solve(string &s1, string &s2, int i, int j, vector<vector<int>> &dp)
{
    // Base case: If either string becomes empty,
    // no more pairs can be formed.
    if (i == 0 || j == 0)
        return 0;

    // Return the stored result if this state
    // has already been computed.
    if (dp[i][j] != -1)
        return dp[i][j];

    // If the current characters match,
    // pair them and move to the remaining prefixes.
    if (s1[i - 1] == s2[j - 1])
        return dp[i][j] = 1 + solve(s1, s2, i - 1, j - 1, dp);

    // Otherwise, skip one character from either string
    // and store the maximum result.
    return dp[i][j] = max(solve(s1, s2, i - 1, j, dp), solve(s1, s2, i, j - 1, dp));
}

int maxMatchingPairs(string &s1, string &s2)
{
    int n = s1.size();
    int m = s2.size();

    // Initialize the DP table with -1.
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, -1));

    // Start recursion from the complete strings.
    return solve(s1, s2, n, m, dp);
}

int main()
{
    string s1 = "*@#";
    string s2 = "*#";

    cout << maxMatchingPairs(s1, s2) << endl;

    return 0;
}
Java
import java.util.Arrays;

public class GFG {

    static int solve(String s1, String s2, int i, int j,
                     int[][] dp)
    {

        // Base case: If either string becomes empty,
        // no more pairs can be formed.
        if (i == 0 || j == 0)
            return 0;

        // Return the stored result if this state
        // has already been computed.
        if (dp[i][j] != -1)
            return dp[i][j];

        // If the current characters match,
        // pair them and move to the remaining prefixes.
        if (s1.charAt(i - 1) == s2.charAt(j - 1))
            return dp[i][j]
                = 1 + solve(s1, s2, i - 1, j - 1, dp);

        // Otherwise, skip one character from either string
        // and store the maximum result.
        return dp[i][j]
            = Math.max(solve(s1, s2, i - 1, j, dp),
                       solve(s1, s2, i, j - 1, dp));
    }

    static int maxMatchingPairs(String s1, String s2)
    {
        int n = s1.length();
        int m = s2.length();

        // Initialize the DP table with -1.
        int[][] dp = new int[n + 1][m + 1];
        for (int[] row : dp)
            Arrays.fill(row, -1);

        // Start recursion from the complete strings.
        return solve(s1, s2, n, m, dp);
    }

    public static void main(String[] args)
    {
        String s1 = "*@#";
        String s2 = "*#";

        System.out.println(maxMatchingPairs(s1, s2));
    }
}
Python
def solve(s1, s2, i, j, dp):

    # Base case: If either string becomes empty,
    # no more pairs can be formed.
    if i == 0 or j == 0:
        return 0

    # Return the stored result if this state
    # has already been computed.
    if dp[i][j] != -1:
        return dp[i][j]

    # If the current characters match,
    # pair them and move to the remaining prefixes.
    if s1[i - 1] == s2[j - 1]:
        dp[i][j] = 1 + solve(s1, s2, i - 1, j - 1, dp)
    else:
        # Otherwise, skip one character from either string
        # and store the maximum result.
        dp[i][j] = max(
            solve(s1, s2, i - 1, j, dp),
            solve(s1, s2, i, j - 1, dp)
        )

    return dp[i][j]


def maxMatchingPairs(s1, s2):
    n = len(s1)
    m = len(s2)

    # Initialize the DP table with -1.
    dp = [[-1] * (m + 1) for _ in range(n + 1)]

    # Start recursion from the complete strings.
    return solve(s1, s2, n, m, dp)


# Driver Code
if __name__ == "__main__":
    s1 = "*@#"
    s2 = "*#"

    print(maxMatchingPairs(s1, s2))
C#
using System;

class GFG {
    static int Solve(string s1, string s2, int i, int j,
                     int[, ] dp)
    {
        // Base case: If either string becomes empty,
        // no more pairs can be formed.
        if (i == 0 || j == 0)
            return 0;

        // Return the stored result if this state
        // has already been computed.
        if (dp[i, j] != -1)
            return dp[i, j];

        // If the current characters match,
        // pair them and move to the remaining prefixes.
        if (s1[i - 1] == s2[j - 1])
            return dp[i, j]
                = 1 + Solve(s1, s2, i - 1, j - 1, dp);

        // Otherwise, skip one character from either string
        // and store the maximum result.
        return dp[i, j]
            = Math.Max(Solve(s1, s2, i - 1, j, dp),
                       Solve(s1, s2, i, j - 1, dp));
    }

    static int maxMatchingPairs(string s1, string s2)
    {
        int n = s1.Length;
        int m = s2.Length;

        // Initialize the DP table with -1.
        int[, ] dp = new int[n + 1, m + 1];
        for (int i = 0; i <= n; i++)
            for (int j = 0; j <= m; j++)
                dp[i, j] = -1;

        // Start recursion from the complete strings.
        return Solve(s1, s2, n, m, dp);
    }

    static void Main()
    {
        string s1 = "*@#";
        string s2 = "*#";

        Console.WriteLine(maxMatchingPairs(s1, s2));
    }
}
JavaScript
function solve(s1, s2, i, j, dp)
{
    // Base case: If either string becomes empty,
    // no more pairs can be formed.
    if (i === 0 || j === 0)
        return 0;

    // Return the stored result if this state
    // has already been computed.
    if (dp[i][j] !== -1)
        return dp[i][j];

    // If the current characters match,
    // pair them and move to the remaining prefixes.
    if (s1[i - 1] === s2[j - 1]) {
        dp[i][j] = 1 + solve(s1, s2, i - 1, j - 1, dp);
    }
    else {
        // Otherwise, skip one character from either string
        // and store the maximum result.
        dp[i][j] = Math.max(solve(s1, s2, i - 1, j, dp),
                            solve(s1, s2, i, j - 1, dp));
    }

    return dp[i][j];
}

function maxMatchingPairs(s1, s2)
{
    const n = s1.length;
    const m = s2.length;

    // Initialize the DP table with -1.
    const dp = Array.from({length : n + 1},
                          () => Array(m + 1).fill(-1));

    // Start recursion from the complete strings.
    return solve(s1, s2, n, m, dp);
}

// Driver code
const s1 = "*@#";
const s2 = "*#";

console.log(maxMatchingPairs(s1, s2));

Output
2

[Expected Approach] Using Bottom Up(DP) - O(n * m) Time and O(n * m) Space

The memoization approach avoids repeated computations but still relies on recursion, which uses additional recursive stack space. To eliminate this overhead, compute the DP states iteratively using a bottom-up approach, where each state is built from previously computed states. This gives the same O(n × m) time complexity while avoiding recursive calls.

  • Create a DP table of size (n + 1) × (m + 1) initialized with 0.
  • Traverse both strings using two nested loops.
  • If the current characters are equal, set dp[i][j] = 1 + dp[i - 1][j - 1].
  • Otherwise, set dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]).
  • After filling the table, return dp[n][m] as the answer.
C++
#include <bits/stdc++.h>
using namespace std;

int maxMatchingPairs(string &s1, string &s2)
{
    int n = s1.size();
    int m = s2.size();

    // DP table where dp[i][j] stores the maximum number of
    // matching pairs using the first i characters of s1
    // and the first j characters of s2.
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));

    // Fill the DP table iteratively.
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            // If the current characters match,
            // pair them and move diagonally.
            if (s1[i - 1] == s2[j - 1])
                dp[i][j] = 1 + dp[i - 1][j - 1];

            // Otherwise, skip one character from either string
            // and take the maximum result.
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    // The bottom-right cell contains the answer.
    return dp[n][m];
}

int main()
{
    string s1 = "*@#";
    string s2 = "*#";

    cout << maxMatchingPairs(s1, s2) << endl;

    return 0;
}
Java
import java.util.*;

public class GFG {
    static int maxMatchingPairs(String s1, String s2)
    {
        int n = s1.length();
        int m = s2.length();

        // DP table where dp[i][j] stores the maximum number
        // of matching pairs using the first i characters of
        // s1 and the first j characters of s2.
        int[][] dp = new int[n + 1][m + 1];

        // Fill the DP table iteratively.
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {

                // If the current characters match,
                // pair them and move diagonally.
                if (s1.charAt(i - 1) == s2.charAt(j - 1))
                    dp[i][j] = 1 + dp[i - 1][j - 1];

                // Otherwise, skip one character from either
                // string and take the maximum result.
                else
                    dp[i][j] = Math.max(dp[i - 1][j],
                                        dp[i][j - 1]);
            }
        }

        // The bottom-right cell contains the answer.
        return dp[n][m];
    }

    public static void main(String[] args)
    {
        String s1 = "*@#";
        String s2 = "*#";

        System.out.println(maxMatchingPairs(s1, s2));
    }
}
Python
def maxMatchingPairs(s1, s2):

    n = len(s1)
    m = len(s2)

    # DP table where dp[i][j] stores the maximum number of
    # matching pairs using the first i characters of s1
    # and the first j characters of s2.
    dp = [[0] * (m + 1) for _ in range(n + 1)]

    # Fill the DP table iteratively.
    for i in range(1, n + 1):
        for j in range(1, m + 1):

            # If the current characters match,
            # pair them and move diagonally.
            if s1[i - 1] == s2[j - 1]:
                dp[i][j] = 1 + dp[i - 1][j - 1]

            # Otherwise, skip one character from either string
            # and take the maximum result.
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    # The bottom-right cell contains the answer.
    return dp[n][m]

# Driver Code
if __name__ == "__main__":

    s1 = "*@#"
    s2 = "*#"

    print(maxMatchingPairs(s1, s2))
C#
using System;

class GFG {
    static int maxMatchingPairs(string s1, string s2)
    {
        int n = s1.Length;
        int m = s2.Length;

        // DP table where dp[i][j] stores the maximum number
        // of matching pairs using the first i characters of
        // s1 and the first j characters of s2.
        int[, ] dp = new int[n + 1, m + 1];

        // Fill the DP table iteratively.
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                // If the current characters match,
                // pair them and move diagonally.
                if (s1[i - 1] == s2[j - 1])
                    dp[i, j] = 1 + dp[i - 1, j - 1];

                // Otherwise, skip one character from either
                // string and take the maximum result.
                else
                    dp[i, j] = Math.Max(dp[i - 1, j],
                                        dp[i, j - 1]);
            }
        }

        // The bottom-right cell contains the answer.
        return dp[n, m];
    }

    static void Main()
    {
        string s1 = "*@#";
        string s2 = "*#";

        Console.WriteLine(maxMatchingPairs(s1, s2));
    }
}
JavaScript
function maxMatchingPairs(s1, s2)
{
    const n = s1.length;
    const m = s2.length;

    // DP table where dp[i][j] stores the maximum number of
    // matching pairs using the first i characters of s1
    // and the first j characters of s2.
    const dp = Array.from({length : n + 1},
                          () => Array(m + 1).fill(0));

    // Fill the DP table iteratively.
    for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= m; j++) {

            // If the current characters match,
            // pair them and move diagonally.
            if (s1[i - 1] === s2[j - 1])
                dp[i][j] = 1 + dp[i - 1][j - 1];

            // Otherwise, skip one character from either
            // string and take the maximum result.
            else
                dp[i][j]
                    = Math.max(dp[i - 1][j], dp[i][j - 1]);
        }
    }

    // The bottom-right cell contains the answer.
    return dp[n][m];
}

// Driver code
const s1 = "*@#";
const s2 = "*#";

console.log(maxMatchingPairs(s1, s2));

Output
2
Comment