2486. Append Characters to String to Make Subsequence - Explanation
Description
You are given two strings s and t consisting of only lowercase English letters.
Return the minimum number of characters that need to be appended to the end of s so that t becomes a subsequence of s.
A subsequence is a string that can be derived from another string by deleting some or no characters without changing the order of the remaining characters.
Example 1:
Input: s = "coaching", t = "coding"
Output: 4Explanation: Append the characters "ding" to the end of s so that s = "coachingding".
Now, t is a subsequence of s (coachingding).
It can be shown that appending any 3 characters to the end of s will never make t a subsequence.
Example 2:
Input: s = "abcde", t = "a"
Output: 0Explanation: t is already a subsequence of s ("abcde").
Example 3:
Input: s = "z", t = "abcde"
Output: 5Explanation: Append the characters "abcde" to the end of s so that s = "zabcde".
Now, t is a subsequence of s (zabcde).
It can be shown that appending any 4 characters to the end of s will never make t a subsequence.
Constraints:
1 <= s.length, t.length <= 100,000sandtconsist of lowercase English letters.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Two Pointers - Used to traverse both strings simultaneously while matching characters
- Subsequences - Understanding what makes one string a subsequence of another
- Greedy Algorithms - Recognizing that matching characters from left to right is optimal
1. Two Pointers
Intuition
To make t a subsequence of s by appending characters, we first need to figure out how much of t is already a subsequence of s. We can greedily match characters from t in s from left to right. Once we know how many characters of t we can match, the remaining characters must be appended.
The greedy approach works because matching earlier characters in s never hurts. If a character from t appears multiple times in s, taking the first occurrence leaves more room for matching subsequent characters.
Algorithm
- Use two pointers:
ifor stringsandjfor stringt. - Iterate through
s. Whenevers[i]matchest[j], move both pointers forward. Otherwise, only advancei. - After scanning
s,jrepresents how many characters oftare already matched. - Return
len(t) - j, the number of characters that need to be appended to make the match.
Time & Space Complexity
- Time complexity:
- Space complexity:
Where and are the lengths of the strings and , respectively.
2. Index Jumping
Intuition
The two-pointer approach scans s character by character, which can be slow if s is long and the characters we need are sparse. We can speed this up by precomputing where each character appears in s. For any position, we can instantly jump to the next occurrence of the character we need.
We build a table where store[i][c] tells us the nearest index at or after position i where character c appears. This lets us skip over irrelevant characters in constant time.
Algorithm
- Build a 2D array
storewherestore[i][c]holds the first occurrence of charactercat or after indexiins. - Fill this table by iterating backward through
s. Each position inherits the next position's data, then updates the current character's entry. - Use two pointers
iandjforsandt. For each character int, usestoreto jump directly to its next occurrence ins. - If a character from
tcannot be found (returns sentinel value), stop matching. - Return
len(t) - j.
class Solution:
def appendCharacters(self, s: str, t: str) -> int:
n, m = len(s), len(t)
store = [[n + 1] * 26 for _ in range(n)]
store[n - 1][ord(s[n - 1]) - ord('a')] = n - 1
for i in range(n - 2, -1, -1):
store[i] = store[i + 1][:]
store[i][ord(s[i]) - ord('a')] = i
i, j = 0, 0
while i < n and j < m:
if store[i][ord(t[j]) - ord('a')] == n + 1:
break
i = store[i][ord(t[j]) - ord('a')] + 1
j += 1
return m - jTime & Space Complexity
- Time complexity:
- Space complexity:
Where and are the lengths of the strings and , respectively.
Common Pitfalls
Returning the Wrong Value After Matching
The result should be the number of unmatched characters in t, not the number of matched characters. After the loop, j represents how many characters were matched, so you need to return len(t) - j, not j.
# Wrong: returning matched count
return j # Returns how many matched, not how many to append
# Correct: return remaining characters
return len(t) - jAdvancing Both Pointers on Mismatch
When characters do not match, only the pointer for s should advance. The pointer for t should stay in place waiting for its character to appear later in s. Advancing both pointers skips characters in t that might match later.
# Wrong: advancing both pointers
if s[i] != t[j]:
i += 1
j += 1 # Skips a character in t that might match later
# Correct: only advance s pointer
if s[i] != t[j]:
i += 1
Sign in to join the discussion