2207. Maximize Number of Subsequences in a String
Problem Description
You have a string text
and a pattern string pattern
that contains exactly 2 characters (both strings contain only lowercase English letters and are 0-indexed).
Your task is to:
- Add exactly one character to
text
- you can choose to add eitherpattern[0]
orpattern[1]
- You can add this character anywhere in
text
(including at the beginning or end) - Find the maximum number of times
pattern
appears as a subsequence in the modified text
A subsequence means you can form the pattern by selecting characters from the text in order (but not necessarily consecutive). For example, if pattern = "ab"
, then in the text "aabb"
, the pattern appears 4 times as a subsequence (we can choose the first 'a' with either 'b', or the second 'a' with either 'b').
The goal is to strategically place one additional character to maximize the count of pattern
subsequences in the resulting string.
For example:
- If
text = "abdcdbc"
andpattern = "ac"
- We could add 'a' at the beginning to get
"aabdcdbc"
, or add 'c' at the end to get"abdcdbcc"
- We need to determine which placement gives us the most "ac" subsequences
Intuition
To understand how to maximize subsequences, let's think about how subsequences of pattern
are formed. For a pattern like "ab"
, each occurrence of 'a' can pair with any 'b' that comes after it. So if we have 3 'a's followed by 2 'b's, we get 3 × 2 = 6 subsequences.
The key insight is that when we traverse the string from left to right:
- Each time we encounter
pattern[1]
, it can form a subsequence with everypattern[0]
we've seen before it - We need to keep track of how many
pattern[0]
characters we've seen so far
As we scan through text
, we maintain two counters:
x
: count ofpattern[0]
seen so fary
: count ofpattern[1]
seen so far
When we hit a pattern[1]
, we add x
to our answer because this new pattern[1]
creates x
new subsequences (one with each previously seen pattern[0]
).
Now for the strategic placement of our one additional character:
- If we add
pattern[0]
at the beginning, it can pair with all existingpattern[1]
characters in the text, giving usy
additional subsequences - If we add
pattern[1]
at the end, it can pair with all existingpattern[0]
characters in the text, giving usx
additional subsequences
Therefore, the optimal strategy is to add whichever character would give us more additional subsequences - that's max(x, y)
.
This greedy approach works because adding at the extremes (beginning for pattern[0]
or end for pattern[1]
) ensures maximum pairing opportunities without disrupting existing subsequence counts.
Learn more about Greedy and Prefix Sum patterns.
Solution Approach
We implement a single-pass traversal algorithm with counting to solve this problem efficiently.
Algorithm Steps:
-
Initialize counters: Set up three variables:
ans = 0
: stores the total count of pattern subsequencesx = 0
: counts occurrences ofpattern[0]
y = 0
: counts occurrences ofpattern[1]
-
Traverse the string: For each character
c
intext
:- If
c == pattern[1]
:- Increment
y
by 1 (we found anotherpattern[1]
) - Add
x
toans
(thispattern[1]
forms subsequences with all previouspattern[0]
s)
- Increment
- If
c == pattern[0]
:- Increment
x
by 1 (we found anotherpattern[0]
)
- Increment
- If
-
Handle the additional character: After traversal, add
max(x, y)
to the answer:- Adding
pattern[0]
at the beginning would createy
new subsequences - Adding
pattern[1]
at the end would createx
new subsequences - We choose the option that maximizes our count
- Adding
Why this order matters:
- We check for
pattern[1]
first and update the answer before checkingpattern[0]
- This handles the edge case where
pattern[0] == pattern[1]
correctly - When both characters are the same, each character acts as both the first and second element of the pattern
Time Complexity: O(n)
where n
is the length of text
- we make a single pass through the string
Space Complexity: O(1)
- we only use a constant amount of extra space for counters
The beauty of this solution lies in its simplicity - by maintaining running counts and understanding how subsequences are formed, we can calculate the result in a single pass without needing to actually modify the string or try different insertion positions.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with text = "abad"
and pattern = "ab"
.
Step 1: Initialize counters
ans = 0
(total subsequences count)x = 0
(count of 'a')y = 0
(count of 'b')
Step 2: Traverse the string character by character
Processing index 0: c = 'a'
- 'a' == pattern[0], so increment
x
x = 1
,y = 0
,ans = 0
Processing index 1: c = 'b'
- 'b' == pattern[1], so:
- Increment
y
:y = 1
- Add
x
toans
:ans = 0 + 1 = 1
(this 'b' forms 1 subsequence with the previous 'a')
- Increment
x = 1
,y = 1
,ans = 1
Processing index 2: c = 'a'
- 'a' == pattern[0], so increment
x
x = 2
,y = 1
,ans = 1
Processing index 3: c = 'd'
- 'd' is neither pattern[0] nor pattern[1], so no changes
x = 2
,y = 1
,ans = 1
Step 3: Determine optimal character placement
After traversal: x = 2
(we have 2 'a's) and y = 1
(we have 1 'b')
Now we decide which character to add:
- If we add 'a' at the beginning → "aabad": This new 'a' can pair with the existing 1 'b', creating 1 additional subsequence
- If we add 'b' at the end → "abadb": This new 'b' can pair with the existing 2 'a's, creating 2 additional subsequences
Since max(x, y) = max(2, 1) = 2
, we add 2 to our answer.
Final Result: ans = 1 + 2 = 3
This means the maximum number of "ab" subsequences we can achieve is 3 (by adding 'b' at the end to get "abadb", which contains the subsequences: a₁b₁, a₁b₂, and a₂b₂).
Solution Implementation
1class Solution:
2 def maximumSubsequenceCount(self, text: str, pattern: str) -> int:
3 # Initialize variables
4 # total_subsequences: count of pattern subsequences found
5 # first_char_count: count of pattern[0] seen so far
6 # second_char_count: count of pattern[1] seen so far
7 total_subsequences = 0
8 first_char_count = 0
9 second_char_count = 0
10
11 # Iterate through each character in text
12 for char in text:
13 # If current character matches the second character of pattern
14 if char == pattern[1]:
15 second_char_count += 1
16 # Add all possible subsequences ending at this position
17 # (pairs with all previous occurrences of pattern[0])
18 total_subsequences += first_char_count
19
20 # If current character matches the first character of pattern
21 # Note: This is checked after to handle case when pattern[0] == pattern[1]
22 if char == pattern[0]:
23 first_char_count += 1
24
25 # Add the optimal choice: insert pattern[0] at beginning or pattern[1] at end
26 # Inserting pattern[0] at beginning pairs with all pattern[1] occurrences
27 # Inserting pattern[1] at end pairs with all pattern[0] occurrences
28 total_subsequences += max(first_char_count, second_char_count)
29
30 return total_subsequences
31
1class Solution {
2 public long maximumSubsequenceCount(String text, String pattern) {
3 long totalCount = 0;
4 int firstCharCount = 0; // Count of pattern[0] seen so far
5 int secondCharCount = 0; // Count of pattern[1] seen so far
6
7 // Traverse through the text to count subsequences
8 for (int i = 0; i < text.length(); i++) {
9 // When we find pattern[1], it can form subsequences with all pattern[0] before it
10 if (text.charAt(i) == pattern.charAt(1)) {
11 secondCharCount++;
12 totalCount += firstCharCount; // Add all possible subsequences ending at this position
13 }
14
15 // Count occurrences of pattern[0]
16 // Note: This is after the pattern[1] check to handle case when pattern[0] == pattern[1]
17 if (text.charAt(i) == pattern.charAt(0)) {
18 firstCharCount++;
19 }
20 }
21
22 // Add the maximum benefit from inserting one character
23 // If we add pattern[0] at the beginning, it pairs with all existing pattern[1]s (secondCharCount)
24 // If we add pattern[1] at the end, it pairs with all existing pattern[0]s (firstCharCount)
25 totalCount += Math.max(firstCharCount, secondCharCount);
26
27 return totalCount;
28 }
29}
30
1class Solution {
2public:
3 long long maximumSubsequenceCount(string text, string pattern) {
4 long long result = 0;
5 int firstCharCount = 0; // Count of pattern[0] seen so far
6 int secondCharCount = 0; // Count of pattern[1] seen so far
7
8 // Traverse through each character in the text
9 for (char& currentChar : text) {
10 // If current character matches the second character of pattern
11 if (currentChar == pattern[1]) {
12 secondCharCount++;
13 // Add all possible subsequences ending at this position
14 // Each pattern[0] before this position can form a subsequence with this pattern[1]
15 result += firstCharCount;
16 }
17
18 // If current character matches the first character of pattern
19 // Note: This is checked after pattern[1] to handle case when pattern[0] == pattern[1]
20 if (currentChar == pattern[0]) {
21 firstCharCount++;
22 }
23 }
24
25 // Add the maximum benefit from prepending pattern[0] or appending pattern[1]
26 // Prepending pattern[0] would create secondCharCount new subsequences
27 // Appending pattern[1] would create firstCharCount new subsequences
28 result += max(firstCharCount, secondCharCount);
29
30 return result;
31 }
32};
33
1/**
2 * Calculates the maximum number of subsequences of a pattern that can be formed
3 * by adding one character (either pattern[0] or pattern[1]) to the text.
4 *
5 * @param text - The input string to search for subsequences
6 * @param pattern - A two-character pattern string
7 * @returns The maximum count of pattern subsequences after adding one character
8 */
9function maximumSubsequenceCount(text: string, pattern: string): number {
10 // Initialize the result counter for subsequences found
11 let subsequenceCount: number = 0;
12
13 // Track occurrences of pattern[0] and pattern[1] in the text
14 let firstCharCount: number = 0; // Count of pattern[0] characters
15 let secondCharCount: number = 0; // Count of pattern[1] characters
16
17 // Iterate through each character in the text
18 for (const currentChar of text) {
19 // When we find pattern[1], it can form subsequences with all previous pattern[0]s
20 if (currentChar === pattern[1]) {
21 secondCharCount++;
22 subsequenceCount += firstCharCount; // Add all possible subsequences ending at this position
23 }
24
25 // Count occurrences of pattern[0] for future subsequence calculations
26 if (currentChar === pattern[0]) {
27 firstCharCount++;
28 }
29 }
30
31 // Add the optimal character: either pattern[0] at the beginning (pairs with all pattern[1]s)
32 // or pattern[1] at the end (pairs with all pattern[0]s)
33 subsequenceCount += Math.max(firstCharCount, secondCharCount);
34
35 return subsequenceCount;
36}
37
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string text
. The algorithm iterates through the string exactly once using a single for loop, performing constant-time operations (comparisons, additions, and variable updates) for each character.
The space complexity is O(1)
. The algorithm uses only a fixed number of variables (ans
, x
, y
, and the loop variable c
) regardless of the input size. No additional data structures that scale with the input are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrect Order of Character Checking
The Problem:
Many developers instinctively check for pattern[0]
first, then pattern[1]
. This seems logical but fails when pattern[0] == pattern[1]
(e.g., pattern = "aa").
Incorrect Implementation:
for char in text: if char == pattern[0]: # Checking pattern[0] first first_char_count += 1 if char == pattern[1]: # Then checking pattern[1] second_char_count += 1 total_subsequences += first_char_count
Why It Fails:
When both pattern characters are the same (like "aa"), checking pattern[0]
first means we increment first_char_count
before adding it to total_subsequences
. This causes us to count a character pairing with itself in the same position, which is invalid for a subsequence.
The Solution:
Always check pattern[1]
first and update the answer, then check pattern[0]
:
for char in text: if char == pattern[1]: # Check pattern[1] FIRST second_char_count += 1 total_subsequences += first_char_count # Use old count if char == pattern[0]: # Then update pattern[0] count first_char_count += 1
Pitfall 2: Misunderstanding the Insertion Strategy
The Problem: Some solutions try to iterate through all possible insertion positions to find the optimal placement, leading to O(n²) complexity.
Incorrect Approach:
max_count = 0
for i in range(len(text) + 1):
# Try inserting at position i
modified = text[:i] + pattern[0] + text[i:]
count = count_subsequences(modified, pattern)
max_count = max(max_count, count)
# Repeat for pattern[1]...
Why It's Inefficient:
There are only two optimal choices: add pattern[0]
at the beginning or add pattern[1]
at the end. Any other position would yield fewer subsequences.
The Solution: Recognize that:
- Adding
pattern[0]
at the beginning creates subsequences with all existingpattern[1]
characters - Adding
pattern[1]
at the end creates subsequences with all existingpattern[0]
characters - Simply take the maximum of these two options:
max(first_char_count, second_char_count)
Pitfall 3: Using 'elif' Instead of Separate 'if' Statements
The Problem:
Using elif
prevents counting when pattern[0] == pattern[1]
.
Incorrect Implementation:
for char in text: if char == pattern[1]: second_char_count += 1 total_subsequences += first_char_count elif char == pattern[0]: # Wrong! Should be 'if' first_char_count += 1
Why It Fails:
When pattern = "aa"
and we encounter an 'a', we need to execute both blocks - the character acts as both the first and second element of the pattern. Using elif
skips the second block.
The Solution:
Use two separate if
statements to ensure both conditions are checked independently:
for char in text: if char == pattern[1]: # Handle as second character if char == pattern[0]: # Separate 'if', not 'elif' # Handle as first character
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!