Dynamic Programming
Minimum Window Subsequence
DESCRIPTION (credit Leetcode.com)
Given two strings s1 and s2, return the minimum contiguous substring of s1 such that s2 is a subsequence of the substring.
If there is no such substring, return an empty string "".
If there are multiple valid substrings of the same minimum length, return the one with the smallest starting index.
A subsequence is a sequence that can be derived from another sequence by deleting some or no elements without changing the order of the remaining elements.
Example 1
Input:
s1 = "abcdebdde" s2 = "bde"
Output:
"bcde"
Explanation: "bcde" is the shortest substring of s1 that contains "bde" as a subsequence.
Example 2
Input:
s1 = "jmeqksfrsdcmsiwvaovztaqenprpvnbstl" s2 = "u"
Output:
""
Explanation: "u" does not appear in s1, so there is no valid substring.
Explanation
- "bcde" works because it contains b...d...e in order
- "bdde" also works (b...d...e)
- We want the shortest one: "bcde" (length 4)
Starting Simple: The Brute Force Approach
brute_force(s1, s2)
for each starting position i in s1
for each ending position j >= i
if s2 is subsequence of s1[i:j+1]
track if this is the shortest so far
return shortest windowA Smarter Approach: Forward Then Backward
- Find where s2 ends - Scan forward through s1, matching characters of s2 one by one until we've matched all of s2
- Shrink from the start - Once we've found a complete match ending at position i, walk backwards to find where this particular match started
The Key Observation
- If this is the first character of s2 (j=0), we're starting a new potential window right here at position i
- If this is a later character of s2 (j>0), we need to know: "Where did the window that matched s2[0..j-1] start?"
Why Dynamic Programming?
- If j == 0: This is the first character of s2, so dp[0] = i (start a new window here)
- If j > 0: We extend a previous match, so dp[j] = dp[j-1] (the start position carries over from the previous match)
Why Iterate Backwards Through s2?
Walkthrough Example
- s1[1]='b' matches s2[0]='b'
- dp[0] = 1 (new window starts at index 1)
- dp = [1, -1, -1]
- s1[3]='d' matches s2[1]='d'
- dp[1] = dp[0] = 1 (window still starts at 1)
- dp = [1, 1, -1]
- s1[4]='e' matches s2[2]='e'
- dp[2] = dp[1] = 1
- dp = [1, 1, 1]
- Complete match! Window from index 1 to 4: "bcde" (length 4)
- s1[5]='b' matches s2[0]='b'
- dp[0] = 5 (new window starts at 5)
- dp = [5, 1, 1]
- s1[6]='d' matches s2[1]='d'
- dp[1] = dp[0] = 5
- dp = [5, 5, 1]
- s1[7]='d' matches s2[1]='d'
- dp[1] = dp[0] = 5
- dp = [5, 5, 1]
- s1[8]='e' matches s2[2]='e'
- dp[2] = dp[1] = 5
- dp = [5, 5, 5]
- Complete match! Window from 5 to 8: "bdde" (length 4)
- Same length as "bcde", but starts later. We keep "bcde".
Visualization
Finding the shortest substring containing s2 as a subsequence
0 / 46
Solution
Test Your Knowledge
Login to take the complexity quiz and track your progress
Complexity Analysis
Time Complexity: O(m × n) where m is the length of s1 and n is the length of s2. For each character in s1, we potentially update all n entries in the dp array.
Space Complexity: O(n) We use a 1D DP array of size n (length of s2).
Alternative Approaches
- Two Pointers with Expansion: Find each occurrence of s2 as a subsequence, then try to shrink from the left. Same time complexity but with higher constants.
- 2D DP: Use dp[i][j] to track starting positions for each (i, j) pair. Uses O(m × n) space but might be conceptually clearer for some.