Number of Pairs of Strings With Concatenation Equal to Target solution and explanation
8 more videos on this problem
Number Of Pairs Of Strings With Concatenation Equal To Target|Leetcode 2023|Biweekly 62|Java| HindiPepcoding · 6:23 · 1K views
2023. Number of Pairs of Strings With Concatenation Equal to Target (Leetcode Medium)Programming Live with Larry · 6:05 · 1K views
2023. Number of Pairs of Strings With Concatenation Equal to Target Leetcode Medium|Biweekly 62| C++Harsh Patel · 4:20 · 648 views
Number of Pairs of Strings With Concatenation Equal to Target | Leetcode | DSA | Arraystechymon · 4:28 · 625 views
LeetCode :Number of Pairs of Strings With Concatenation Equal to Target |JavaJoseph Fifstones · 23:48 · 290 views
Leetcode 2023: Number of Pairs of Strings With Concatenation Equal to Target (Live Coding)CS Live Coding · 13:23 · 262 views
2023. Number of Pairs of Strings With Concatenation Equal to TargetAaron Sadino Coding · 13:10 · 100 views
Leetcode 2023 Number of Pairs With Concatenation Equal to Target. Meta/UberRoger Sentongo (rnt) · 14:13 · 86 views
Problem #2023 - Number of Pairs of Strings With Concatenation Equal to TargetHaoTheCoder · 2:01 · 40 views
The problem
Given an array of digit strings nums and a digit string target, return the number of pairs of indices (i, j) (where i != j) such that the concatenation of nums[i] + nums[j] equals target.
Example 1:
Input: nums = ["777","7","77","77"], target = "7777" Output: 4 Explanation: Valid pairs are: - (0, 1): "777" + "7" - (1, 0): "7" + "777" - (2, 3): "77" + "77" - (3, 2): "77" + "77"
Example 2:
Input: nums = ["123","4","12","34"], target = "1234" Output: 2 Explanation: Valid pairs are: - (0, 1): "123" + "4" - (2, 3): "12" + "34"
Example 3:
Input: nums = ["1","1","1"], target = "11" Output: 6 Explanation: Valid pairs are: - (0, 1): "1" + "1" - (1, 0): "1" + "1" - (0, 2): "1" + "1" - (2, 0): "1" + "1" - (1, 2): "1" + "1" - (2, 1): "1" + "1"
Constraints:
2 <= nums.length <= 1001 <= nums[i].length <= 1002 <= target.length <= 100nums[i]andtargetconsist of digits.nums[i]andtargetdo not have leading zeros.
Approach overview
Problem Overview: You get an array nums of strings and a string target. Count ordered pairs (i, j) where i != j and nums[i] + nums[j] == target. The challenge is avoiding the obvious O(n²) pair check.
Approach 1: Divide and Conquer on Target Splits (Hash Counting) (Time: O(n + m), Space: O(n))
The key observation: any valid pair must split the target into a prefix and suffix. If target = prefix + suffix, then you only need strings equal to prefix and suffix. Build a frequency map using a hash table for all strings in nums. Iterate over every possible split of the target string. For each split, multiply the counts of the prefix and suffix from the map.
Special care is required when prefix == suffix. In that case, pairs must use different indices, so the number of ordered pairs becomes count * (count - 1). This approach effectively divides the target into independent prefix/suffix subproblems and uses constant-time hash lookups to count matches.
The runtime depends mainly on building the frequency map and scanning the target splits. Hash lookups are O(1) on average, making this the most practical solution. It heavily relies on efficient string matching and frequency counting.
Approach 2: Iterative In-Place Sorting with Binary Search (Time: O(n log n + n * k), Space: O(1) or O(log n))
Another approach sorts the array of strings first. After sorting, iterate through each string and check whether it can form the prefix of target. If it matches the prefix, compute the required suffix and locate it using binary search in the sorted array.
Because the array is sorted, duplicate strings appear in contiguous ranges. You can count how many matches exist for the suffix and accumulate valid pair counts while ensuring indices are different when prefix and suffix are identical. This method avoids an explicit hash map and keeps auxiliary memory minimal.
The tradeoff is additional sorting cost and repeated substring checks on the string. Still, it performs well when memory usage must stay low or when the input is already partially sorted.
Recommended for interviews: The hash table counting approach based on splitting the target is what most interviewers expect. It reduces the naive O(n²) pair comparison to near linear time and clearly demonstrates pattern recognition with prefix/suffix decomposition. Showing the brute-force idea first proves correctness, but the hash-based counting solution demonstrates stronger algorithmic thinking.
Approach 1: Divide and Conquer Approach
This approach employs the classic divide and conquer strategy to efficiently solve the problem by breaking it down into smaller sub-problems. Each smaller problem is solved independently before combining their results to arrive at the final solution. Common examples include algorithms like merge sort or quicksort.
def merge(arr, left, mid, right):
L = arr[left:mid+1]
R = arr[mid+1:right+1]
i = j = 0
k = left
while i < len(L) and j < len(R):
if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
def merge_sort(arr, left, right):
if left < right:
mid = (left + right) // 2
merge_sort(arr, left, mid)
merge_sort(arr, mid + 1, right)
merge(arr, left, mid, right)
arr = [12, 11, 13, 5, 6, 7]
merge_sort(arr, 0, len(arr) - 1)
print(arr)import java.util.Arrays;
public class MergeSort {
public void merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int[] L = new int[n1];
int[] R = new int[n2];
System.arraycopy(arr, left, L, 0, n1);
System.arraycopy(arr, mid + 1, R, 0, n2);
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j])
arr[k++] = L[i++];
else
arr[k++] = R[j++];
}
while (i < n1)
arr[k++] = L[i++];
while (j < n2)
arr[k++] = R[j++];
}
public void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
MergeSort ob = new MergeSort();
ob.mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int> &arr, int start, int mid, int end) {
vector<int> left(arr.begin() + start, arr.begin() + mid + 1);
vector<int> right(arr.begin() + mid + 1, arr.begin() + end + 1);
int i = 0, j = 0, k = start;
while (i < left.size() && j < right.size()) {
if (left[i] <= right[j])
arr[k++] = left[i++];
else
arr[k++] = right[j++];
}
while (i < left.size())
arr[k++] = left[i++];
while (j < right.size())
arr[k++] = right[j++];
}
void mergeSort(vector<int> &arr, int start, int end) {
if (start < end) {
int mid = start + (end - start) / 2;
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
merge(arr, start, mid, end);
}
}
int main() {
vector<int> arr = {12, 11, 13, 5, 6, 7};
mergeSort(arr, 0, arr.size() - 1);
for (int num : arr)
cout << num << " ";
return 0;
}function merge(arr, left, mid, right) {
let n1 = mid - left + 1;
let n2 = right - mid;
let L = [], R = [];
for (let i = 0; i < n1; i++)
L[i] = arr[left + i];
for (let j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
let i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
function mergeSort(arr, left, right) {
if (left < right) {
let mid = Math.floor((left + right) / 2);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
let arr = [12, 11, 13, 5, 6, 7];
mergeSort(arr, 0, arr.length - 1);
console.log(arr);#include <stdio.h>
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int L[n1], R[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[left + i];
for (int j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
for (int i = 0; i < arr_size; i++)
printf("%d ", arr[i]);
return 0;
}using System;
class MergeSortExample {
static void Merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int[] L = new int[n1];
int[] R = new int[n2];
Array.Copy(arr, left, L, 0, n1);
Array.Copy(arr, mid + 1, R, 0, n2);
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j])
arr[k++] = L[i++];
else
arr[k++] = R[j++];
}
while (i < n1)
arr[k++] = L[i++];
while (j < n2)
arr[k++] = R[j++];
}
static void MergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
MergeSort(arr, left, mid);
MergeSort(arr, mid + 1, right);
Merge(arr, left, mid, right);
}
}
public static void Main() {
int[] arr = {12, 11, 13, 5, 6, 7};
MergeSort(arr, 0, arr.Length - 1);
Console.WriteLine(String.Join(" ", arr));
}
}This Python solution follows the same divide and conquer strategy employed in other languages, relying on slicing for temporary sub-lists. The recursion in merge_sort operates similarly, dividing the array and merging sorted pieces together through the merge function.
Time Complexity: O(n log n) - As it involves dividing the array and merging over logarithmic depth and linear operations.
Space Complexity: O(n) - Additional storage space is taken by temporary slices.
Approach 2: Iterative In-Place Sorting
This approach focuses on sorting algorithms that perform sorting in-place, using an iterative technique instead of recursion. These algorithms generally have lower space overhead as they operate directly on the input data and are characterized by the use of loops to iterate over the array.
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and key < arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
arr = [12, 11, 13, 5, 6]
insertion_sort(arr)
print(arr)public class SelectionSort {
public static void selectionSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < arr.length; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6};
selectionSort(arr);
for (int num : arr)
System.out.print(num + " ");
}
}#include <iostream>
#include <vector>
using namespace std;
void bubbleSort(vector<int> &arr) {
bool swapped;
for (int i = 0; i < arr.size() - 1; i++) {
swapped = false;
for (int j = 0; j < arr.size() - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
swapped = true;
}
}
if (!swapped)
break;
}
}
int main() {
vector<int> arr = {12, 11, 13, 5, 6};
bubbleSort(arr);
for (int num : arr)
cout << num << " ";
return 0;
}function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
let key = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
let arr = [12, 11, 13, 5, 6];
insertionSort(arr);
console.log(arr);#include <stdio.h>
void insertionSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6};
int n = sizeof(arr) / sizeof(arr[0]);
insertionSort(arr, n);
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
return 0;
}using System;
class SortingExample {
static void SelectionSort(int[] arr) {
for (int i = 0; i < arr.Length - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < arr.Length; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
public static void Main() {
int[] arr = {12, 11, 13, 5, 6};
SelectionSort(arr);
Console.WriteLine(String.Join(" ", arr));
}
}This Python example of insertion sort efficiently sorts the array in-place by traversing the array repeatedly and inserting each new element in its proper position relative to the previously sorted elements, utilizing the key variable to hold the current element being compared.
Time Complexity: O(n^2) - Each pair of elements is compared during the sorting process.
Space Complexity: O(1) - The algorithm sorts in situ, without external array usage for temporary storage.
Approach 3: Enumeration
Traverse the array nums, for each i, enumerate all j, if i neq j and nums[i] + nums[j] = target, then increment the answer by one.
The time complexity is O(n^2 times m), where n and m are the lengths of the array nums and the string target, respectively. The space complexity is O(1).
func numOfPairs(nums []string, target string) (ans int) {
for i, a := range nums {
for j, b := range nums {
if i != j && a+b == target {
ans++
}
}
}
return ans
}Approach 4: Hash Table
We can use a hash table to count the occurrence of each string in the array nums, then traverse all prefixes and suffixes of the string target. If both the prefix and suffix are in the hash table, then increment the answer by the product of their occurrences.
The time complexity is O(n + m^2), and the space complexity is O(n). Here, n and m are the lengths of the array nums and the string target, respectively.
func numOfPairs(nums []string, target string) (ans int) {
cnt := map[string]int{}
for _, x := range nums {
cnt[x]++
}
for i := 1; i < len(target); i++ {
a, b := target[:i], target[i:]
if a != b {
ans += cnt[a] * cnt[b]
} else {
ans += cnt[a] * (cnt[a] - 1)
}
}
return
}Complexity comparison
| Approach | Complexity |
|---|---|
| Divide and Conquer Approach | Time Complexity: O(n log n) - As it involves dividing the array and merging over logarithmic depth and linear operations. |
| Iterative In-Place Sorting | Time Complexity: O(n^2) - Each pair of elements is compared during the sorting process. |
| Enumeration | – |
| Hash Table | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Pair Check | O(n² * k) | O(1) | Small inputs or initial reasoning during interviews |
| Divide and Conquer on Target Splits (Hash Map Counting) | O(n + m) | O(n) | General case; fastest and most common interview solution |
| Iterative In-Place Sorting + Binary Search | O(n log n + n * k) | O(1)–O(log n) | When memory is constrained or hash tables are undesirable |
Frequently asked questions
Is Number of Pairs of Strings With Concatenation Equal to Target easy or hard?
This problem is rated Medium because the brute-force solution is straightforward but inefficient at O(n²). Recognizing that the target can be split into prefix and suffix components and solved using hash-map counting is the key insight that reduces the complexity significantly.
Number of Pairs of Strings With Concatenation Equal to Target Python/Java solution
Most Python and Java implementations build a HashMap or dictionary of string frequencies. The algorithm then iterates over every possible split of the target and counts matching prefix–suffix pairs using constant-time lookups. This implementation typically runs in O(n + m) time and O(n) space.
How to solve Number of Pairs of Strings With Concatenation Equal to Target in O(n)?
Store every string in a hash map with its frequency. Iterate through every possible split of the target string into prefix and suffix. For each split, retrieve the counts of both parts from the map and add their pair combinations, adjusting when prefix equals suffix to avoid using the same index twice.
What is the best approach for Number of Pairs of Strings With Concatenation Equal to Target?
The most efficient approach uses a hash table to store frequencies of all strings in the array. Split the target into every possible prefix and suffix and look up both parts in the map. Multiply their counts to compute how many pairs form the target. This reduces the complexity to roughly O(n + m) with O(n) extra space.
Is Number of Pairs of Strings With Concatenation Equal to Target asked at Google/Amazon/Meta?
String pairing and hash-map counting problems are common interview patterns at companies like Google, Amazon, and Meta. Variations often involve concatenation checks, prefix–suffix decomposition, or counting valid string combinations efficiently.
What data structure is used in Number of Pairs of Strings With Concatenation Equal to Target?
The primary data structure is a hash table (hash map) that stores the frequency of each string in the input array. It enables constant-time lookup when checking whether a required prefix or suffix exists. Arrays and basic string operations are also used for iteration and substring checks.
What is the time complexity of Number of Pairs of Strings With Concatenation Equal to Target?
The optimal hash-map counting solution runs in O(n + m) time, where n is the number of strings and m is the length of the target. Building the frequency map takes O(n), and checking all prefix–suffix splits of the target takes O(m). Space complexity is O(n) for storing string counts.
