Get Equal Substrings Within Budget

Problem

Given two strings s and t of the same length and an integer maxCost, return the maximum length of a substring of s that can be changed to the corresponding substring of t with a total cost less than or equal to maxCost, where the cost of changing a character is the absolute difference of their ASCII values.

  • 1 ≤ s.length, t.length ≤ 10⁵
  • s.length == t.length
  • 0 ≤ maxCost ≤ 10⁶
  • s and t consist of lowercase English letters only

Example

Input: s = "abcd", t = "bcdf", maxCost = 3
Output: 3

Calculate the cost array as [1,1,1,2] representing the absolute differences between corresponding characters. Using a sliding window, the substring from index 0 to 2 ('abc' to 'bcd') has a total cost of 3, which is within maxCost. Extending to index 3 would exceed maxCost. Thus, the maximum length is 3.

Approach

Straightforward Solution

A brute-force approach would check every substring, calculate its cost, and track the maximum length under maxCost. This approach is O(n^2) and infeasible for large inputs.

Core Observation

The problem reduces to finding the longest contiguous subarray where the sum of absolute differences between s and t characters is at most maxCost. This is a classic sliding window problem where the window expands while the cost is affordable and contracts when it exceeds maxCost.

Path to Optimal

Recognizing the problem as a longest subarray with sum constraint allows the use of a sliding window. By maintaining two pointers and a running cost sum, the window expands by moving the right pointer and contracts by moving the left pointer when the cost exceeds maxCost. This approach achieves O(n) time complexity.

Optimal Approach

Use two pointers, left and right, to define a sliding window over the strings. Incrementally add the cost of the character difference at right. If the total cost exceeds maxCost, move left forward, subtracting the cost of the character difference at left. Track the maximum window size where the cost is within maxCost. This guarantees a single pass with O(n) time and O(1) extra space.

Time

O(n)

Each character is visited at most twice: once when the right pointer expands the window and once when the left pointer contracts it, resulting in linear time.

Space

O(1)

Only a few integer variables are used to track pointers and running cost; no additional data structures proportional to input size are needed.

Pattern Spotlight

Sliding Window (Variable Size Window with Sum Constraint)

When asked for the longest or shortest contiguous subarray satisfying a sum or cost constraint, use a sliding window that expands until the constraint is violated, then contracts from the left to restore feasibility, maintaining a running sum to achieve O(n) time.

Solution

1class Solution:
2 def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
3 left = 0
4 cost = 0
5 best = 0
6
7 for right in range(len(s)):
8 cost += abs(ord(s[right]) - ord(t[right]))
9
10 while cost > maxCost:
11 cost -= abs(ord(s[left]) - ord(t[left]))
12 left += 1
13
14 best = max(best, right - left + 1)
15
16 return best

Step-by-Step Solution

1

Initialize Sliding Window Pointers and Cost Accumulator

3left = 0
4cost = 0
5best = 0

Objective

To set up the initial state for the sliding window traversal, including the left pointer, running cost, and best maximum length found.

Key Insight

Starting with left at 0 and cost at 0 establishes the initial empty window. The best variable tracks the longest valid substring length found so far. This setup is essential to begin the incremental expansion and contraction of the window.

Interview Quick-Check

Core Logic

Initializing pointers and accumulators before the loop is critical to correctly track the window boundaries and cost.

State & Boundaries

left pointer starts at 0, representing the start of the window; cost starts at 0 since no characters are included yet.

2

Expand Window and Accumulate Cost While Maintaining Constraint

7for right in range(len(s)):
8 cost += abs(ord(s[right]) - ord(t[right]))
10 while cost > maxCost:
11 cost -= abs(ord(s[left]) - ord(t[left]))
12 left += 1

Objective

To iterate over the string with the right pointer, adding character difference costs and adjusting the left pointer to maintain the maxCost constraint.

Key Insight

By adding the absolute difference of characters at the right pointer, the window cost increases. If the cost exceeds maxCost, the window is shrunk from the left by subtracting the left character's cost and moving left forward. This dynamic adjustment ensures the window always represents the longest valid substring ending at right.

Interview Quick-Check

Core Logic

The sliding window expands by moving right and accumulates cost, then contracts by moving left when cost exceeds maxCost, maintaining a valid window.

State & Boundaries

The while loop condition ensures the window is shrunk until the cost is within maxCost, preserving correctness.

Common Pitfalls & Bugs

Forgetting to subtract the left character's cost when moving left pointer leads to incorrect cost tracking and invalid windows.

Complexity

Each character is processed at most twice, ensuring O(n) time complexity.

3

Update Maximum Valid Substring Length

14best = max(best, right - left + 1)

Objective

To record the longest substring length found so far that satisfies the cost constraint.

Key Insight

After adjusting the window to be valid, the length of the current window (right - left + 1) is a candidate for the maximum length. Updating best with the maximum ensures the algorithm returns the longest valid substring length after processing all characters.

Interview Quick-Check

Core Logic

Updating the maximum length after each valid window expansion ensures the final answer reflects the longest feasible substring.

State & Boundaries

The window length calculation accounts for zero-based indexing by adding 1.

4

Return the Maximum Length Found

16return best

Objective

To output the final result after processing the entire string.

Key Insight

Returning best after the loop completes provides the maximum length of any substring meeting the cost constraint. This final step concludes the algorithm.

Interview Quick-Check

Core Logic

Returning the tracked maximum length is the culmination of the sliding window process.

Line Analysis

This solution has 2 Critical lines interviewers watch for.

Line 8 Critical
cost += abs(ord(s[right]) - ord(t[right]))

Add the absolute difference of the characters at the right pointer to the running cost.

Incrementally updating the cost with the difference at the new right boundary allows efficient tracking of the total cost of the current window without recomputing from scratch.

Line 10 Critical
while cost > maxCost:

While the running cost exceeds maxCost, shrink the window from the left.

This loop ensures the window remains valid by removing characters from the left until the total cost is within the allowed budget, maintaining correctness.

Line 11 Core
cost -= abs(ord(s[left]) - ord(t[left]))

Subtract the absolute difference of the characters at the left pointer from the running cost.

Removing the leftmost character's cost from the total cost correctly updates the window's cost as it shrinks, preventing overcounting.

Line 14 Core
best = max(best, right - left + 1)

Update the best maximum substring length with the current window size if larger.

Recording the maximum window size found so far ensures the algorithm returns the longest valid substring length after processing all characters.

Line 4 Core
cost = 0

Initialize the running cost accumulator to 0.

The cost variable tracks the total cost of changing characters in the current window; starting at 0 reflects an empty window with no cost.

Line 12 Core
left += 1

Move the left pointer forward to shrink the window.

Advancing the left pointer reduces the window size, enabling the algorithm to restore feasibility when the cost exceeds maxCost.

Line 16 Core
return best

Return the maximum substring length found after processing the entire string.

This final return statement outputs the correct answer, concluding the algorithm.

Line 3 Standard
left = 0

Initialize the left pointer of the sliding window to 0.

Starting the left pointer at 0 sets the initial window boundary, enabling the window to expand from the start of the string.

Line 5 Standard
best = 0

Initialize the variable to track the best (maximum) substring length found.

Tracking the best length is essential to record the longest valid substring as the window moves through the string.

Line 7 Standard
for right in range(len(s)):

Iterate over the string with the right pointer to expand the sliding window.

The right pointer moves forward to include new characters, enabling exploration of all possible substrings in a single pass.

Test Your Understanding

Why does moving the left pointer forward when the cost exceeds maxCost guarantee that the window remains valid and that the maximum length is found?

Because the cost sum is strictly reduced by removing the leftmost character's cost, restoring feasibility. This process ensures all valid windows are considered, and the maximum length is updated only when the window is valid, guaranteeing correctness.

Related Problems

Sliding Window pattern

Don't just read it. Drill it.

Reconstruct Get Equal Substrings Within Budget from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Get Equal Substrings Within Budget drill

or drill a free problem