2024. Maximize the Confusion of an Exam
Problem Description
You have a test answer key consisting of n
true/false questions, where each answer is represented as either 'T'
(true) or 'F'
(false). The goal is to maximize the length of consecutive questions that have the same answer.
Given:
- A string
answerKey
whereanswerKey[i]
represents the original answer to questioni
- An integer
k
representing the maximum number of answer changes allowed
You can perform the following operation at most k
times:
- Change any answer in the answer key from
'T'
to'F'
or from'F'
to'T'
The task is to find the maximum possible length of consecutive 'T'
s or consecutive 'F'
s that can be achieved after performing at most k
changes.
For example, if answerKey = "TTFTTFTT"
and k = 1
, you could change one 'F'
to 'T'
to create a sequence of 5 consecutive 'T'
s: "TTTTTFTT"
or "TTFTTTTT"
. The maximum consecutive length would be 5.
The problem essentially asks you to find the longest substring where all characters can be made the same (either all 'T'
or all 'F'
) by changing at most k
characters.
Intuition
The key insight is that we want to find the longest substring where we can make all characters the same by changing at most k
characters. Since we only have two possible characters ('T'
and 'F'
), we need to consider both possibilities: making all characters 'T'
or making all characters 'F'
.
Think of it this way: if we're trying to make a substring all 'T'
s, then we can tolerate at most k
'F'
s in that substring. Similarly, if we're trying to make it all 'F'
s, we can tolerate at most k
'T'
s.
This naturally leads to a sliding window approach. We can maintain a window and expand it as long as the number of characters we need to change (the minority character in the window) doesn't exceed k
. When it does exceed k
, we need to shrink the window from the left.
The clever part of the solution is that instead of explicitly tracking both the window size and positions, we can use a simplified sliding window that never actually shrinks in size - it only slides forward. When we encounter too many characters to change (more than k
), we move both the left and right boundaries forward by one position, maintaining the window size. This way, the window naturally grows to its maximum possible size and stays there.
We run this sliding window logic twice: once to find the longest substring that can be made all 'T'
s (by tolerating up to k
'F'
s), and once to find the longest substring that can be made all 'F'
s (by tolerating up to k
'T'
s). The answer is the maximum of these two values.
This approach works because we're essentially asking: "What's the longest substring where the minority character appears at most k
times?" And since we don't know which character should be the minority, we try both possibilities.
Learn more about Binary Search, Prefix Sum and Sliding Window patterns.
Solution Approach
The solution implements a sliding window technique with a helper function f(c)
that finds the maximum length of consecutive characters when we can change at most k
characters to character c
.
Here's how the algorithm works:
Helper Function f(c)
:
- Initialize two variables:
cnt
(count of characterc
in current window) andl
(left pointer of the window, starting at 0) - Iterate through each character in
answerKey
using an implicit right pointer - For each character:
- If it matches
c
, incrementcnt
- If
cnt > k
(meaning we need to change more thank
characters), we need to adjust the window:- Check if the character at the left boundary (
answerKey[l]
) isc
- If it is, decrement
cnt
(removing it from our count) - Move the left pointer right by incrementing
l
- Check if the character at the left boundary (
- If it matches
- Return
len(answerKey) - l
, which gives us the final window size
Key Insight of the Window Logic:
The window never actually shrinks - when we exceed k
changes needed, we slide the window forward by moving both boundaries. This maintains the maximum window size found so far. At the end, len(answerKey) - l
gives us this maximum size.
Main Function:
- Call
f("T")
to find the maximum consecutive length when changing characters to 'T' - Call
f("F")
to find the maximum consecutive length when changing characters to 'F' - Return the maximum of these two values
Example Walkthrough:
For answerKey = "TTFTTFTT"
and k = 1
:
f("T")
: We can tolerate 1 'F'. The window grows until we hit the second 'F', then slides forward maintaining size 5f("F")
: We can tolerate 1 'T'. The best we can do is a window of size 2 (the two 'F's)- Result:
max(5, 2) = 5
Time Complexity: O(n)
where n
is the length of answerKey
, as we traverse the string twice (once for each character type)
Space Complexity: O(1)
as we only use a constant amount of extra space for variables
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 the solution with answerKey = "TFFT"
and k = 1
:
Goal: Find the longest consecutive sequence of same characters after at most 1 change.
Step 1: Try making all characters 'T' using f("T")
We can tolerate at most 1 'F' in our window.
-
Position 0: Character is 'T'
- Window: [T], cnt = 1, l = 0
- 'T' matches our target, cnt becomes 1
-
Position 1: Character is 'F'
- Window: [T,F], cnt = 1, l = 0
- 'F' doesn't match, cnt stays 1
- We have 1 'F' in window (≤ k=1), so it's valid
-
Position 2: Character is 'F'
- Window: [T,F,F], cnt = 1, l = 0
- 'F' doesn't match, cnt stays 1
- We have 2 'F's in window (> k=1), need to slide!
- Check answerKey[l=0] = 'T', it matches 'T', so cnt becomes 0
- Move l to 1
- New window: [F,F], cnt = 0, l = 1
-
Position 3: Character is 'T'
- Window: [F,F,T], cnt = 1, l = 1
- 'T' matches our target, cnt becomes 1
Final: len(answerKey) - l = 4 - 1 = 3
Step 2: Try making all characters 'F' using f("F")
We can tolerate at most 1 'T' in our window.
-
Position 0: Character is 'T'
- Window: [T], cnt = 0, l = 0
- 'T' doesn't match 'F', cnt stays 0
-
Position 1: Character is 'F'
- Window: [T,F], cnt = 1, l = 0
- 'F' matches our target, cnt becomes 1
- We have 1 'T' in window (≤ k=1), valid
-
Position 2: Character is 'F'
- Window: [T,F,F], cnt = 2, l = 0
- 'F' matches our target, cnt becomes 2
- We have 1 'T' in window (≤ k=1), valid
-
Position 3: Character is 'T'
- Window: [T,F,F,T], cnt = 2, l = 0
- 'T' doesn't match 'F', cnt stays 2
- We have 2 'T's in window (> k=1), need to slide!
- Check answerKey[l=0] = 'T', doesn't match 'F', cnt stays 2
- Move l to 1
- New window: [F,F,T], cnt = 2, l = 1
Final: len(answerKey) - l = 4 - 1 = 3
Result: max(3, 3) = 3
We can achieve a consecutive sequence of length 3 by either:
- Changing the middle 'F' to 'T': "TFFT" → "TTTT" (positions 0-2)
- Changing the first 'T' to 'F': "TFFT" → "FFFT" (positions 0-2)
Solution Implementation
1class Solution:
2 def maxConsecutiveAnswers(self, answerKey: str, k: int) -> int:
3 """
4 Find the maximum number of consecutive 'T's or 'F's after flipping at most k answers.
5
6 Args:
7 answerKey: String containing 'T' and 'F' characters
8 k: Maximum number of flips allowed
9
10 Returns:
11 Maximum length of consecutive same answers possible
12 """
13
14 def find_max_consecutive(target_char: str) -> int:
15 """
16 Find maximum consecutive length when converting to target_char.
17 Uses sliding window technique to maintain at most k different characters.
18
19 Args:
20 target_char: The character we want to make consecutive ('T' or 'F')
21
22 Returns:
23 Maximum window size possible with at most k flips
24 """
25 # Count of characters that need to be flipped (different from target)
26 flip_count = 0
27 # Left pointer of sliding window
28 left = 0
29
30 # Expand window with right pointer
31 for right, char in enumerate(answerKey):
32 # If current character needs to be flipped, increment counter
33 if char == target_char:
34 flip_count += 1
35
36 # If we exceed k flips, shrink window from left
37 if flip_count > k:
38 # Remove leftmost character from window
39 if answerKey[left] == target_char:
40 flip_count -= 1
41 left += 1
42
43 # Final window size is the maximum consecutive length
44 return len(answerKey) - left
45
46 # Return maximum of making all 'T's or all 'F's
47 return max(find_max_consecutive("T"), find_max_consecutive("F"))
48
1class Solution {
2 private char[] answerArray;
3 private int maxOperations;
4
5 public int maxConsecutiveAnswers(String answerKey, int k) {
6 // Convert string to char array for efficient access
7 answerArray = answerKey.toCharArray();
8 this.maxOperations = k;
9
10 // Find the maximum between:
11 // 1. Making all answers 'T' by changing at most k 'F's
12 // 2. Making all answers 'F' by changing at most k 'T's
13 return Math.max(
14 findMaxConsecutive('T'),
15 findMaxConsecutive('F')
16 );
17 }
18
19 /**
20 * Finds the maximum consecutive length when changing at most k characters
21 * that are different from the target character
22 *
23 * @param targetChar The character we want to keep (and change others to)
24 * @return Maximum length of consecutive characters after operations
25 */
26 private int findMaxConsecutive(char targetChar) {
27 int leftPointer = 0; // Start of the sliding window
28 int countOfTarget = 0; // Count of target character in current window
29
30 // Expand window by moving through the array
31 for (char currentChar : answerArray) {
32 // If current character matches target, increment count
33 if (currentChar == targetChar) {
34 countOfTarget++;
35 }
36
37 // If we have more than k non-target characters in the window,
38 // shrink the window from the left
39 if (countOfTarget > maxOperations) {
40 // Remove the leftmost character from window
41 if (answerArray[leftPointer] == targetChar) {
42 countOfTarget--;
43 }
44 leftPointer++;
45 }
46 }
47
48 // The final window size is the maximum possible length
49 return answerArray.length - leftPointer;
50 }
51}
52
1class Solution {
2public:
3 int maxConsecutiveAnswers(string answerKey, int k) {
4 int n = answerKey.size();
5
6 // Lambda function to find max consecutive length when changing at most k occurrences of character c
7 auto findMaxConsecutive = [&](char targetChar) {
8 int left = 0; // Left pointer of sliding window
9 int changeCount = 0; // Count of characters that need to be changed (characters equal to targetChar)
10
11 // Iterate through the string with right pointer
12 for (char& currentChar : answerKey) {
13 // If current character matches target, increment change count
14 changeCount += (currentChar == targetChar);
15
16 // If we exceed k changes, shrink window from left
17 if (changeCount > k) {
18 // Move left pointer and adjust count if the left character was a target character
19 changeCount -= (answerKey[left++] == targetChar);
20 }
21 }
22
23 // Return the maximum window size (total length - left pointer position)
24 return n - left;
25 };
26
27 // Try both possibilities: changing T's to F's and changing F's to T's
28 // Return the maximum of both scenarios
29 return max(findMaxConsecutive('T'), findMaxConsecutive('F'));
30 }
31};
32
1/**
2 * Finds the maximum number of consecutive answers after performing at most k operations
3 * @param answerKey - String containing 'T' and 'F' characters
4 * @param k - Maximum number of operations allowed
5 * @returns Maximum length of consecutive same answers
6 */
7function maxConsecutiveAnswers(answerKey: string, k: number): number {
8 const length = answerKey.length;
9
10 /**
11 * Helper function to find maximum consecutive length when converting to target character
12 * @param targetChar - The character we want to make consecutive ('T' or 'F')
13 * @returns Maximum consecutive length achievable
14 */
15 const findMaxConsecutive = (targetChar: string): number => {
16 let leftPointer = 0;
17 let operationCount = 0;
18
19 // Iterate through each character in the answer key
20 for (const currentChar of answerKey) {
21 // Increment operation count if current character differs from target
22 operationCount += currentChar === targetChar ? 0 : 1;
23
24 // If operations exceed k, shrink window from left
25 if (operationCount > k) {
26 // Decrement operation count if left character differs from target
27 operationCount -= answerKey[leftPointer] === targetChar ? 0 : 1;
28 leftPointer++;
29 }
30 }
31
32 // Window size is the difference between total length and left pointer
33 return length - leftPointer;
34 };
35
36 // Return maximum of converting all to 'T' or all to 'F'
37 return Math.max(findMaxConsecutive('T'), findMaxConsecutive('F'));
38}
39
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the answerKey
string.
The algorithm calls the helper function f
twice - once with "T" and once with "F". Each call to f
performs a single pass through the answerKey
string using a sliding window approach. In each iteration of the loop, we perform constant time operations (comparison, addition/subtraction, and index access). Even though there's a conditional increment of the left pointer l
, each character is visited at most twice (once by the right pointer implicitly through the loop, and potentially once when the left pointer moves past it). This gives us O(2n)
which simplifies to O(n)
for each call to f
. Since we call f
twice, the total time complexity is O(2n) = O(n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space. The helper function f
uses two integer variables (cnt
and l
) regardless of the input size. The parameter c
is a single character. No additional data structures that grow with the input size are used. Therefore, the space complexity is constant O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Character Counting Logic
The Pitfall: The most common mistake is counting the wrong characters. The helper function should count characters that are already the target character (don't need changing), not the characters that need to be flipped. Many developers mistakenly count the characters that need to be changed.
Incorrect Implementation:
def find_max_consecutive(target_char: str) -> int:
flip_count = 0 # Counting flips needed
left = 0
for right, char in enumerate(answerKey):
# WRONG: Counting characters that need flipping
if char != target_char:
flip_count += 1
# This logic becomes inverted
if flip_count > k:
if answerKey[left] != target_char:
flip_count -= 1
left += 1
return len(answerKey) - left
Correct Implementation:
def find_max_consecutive(target_char: str) -> int:
same_count = 0 # Count characters already matching target
left = 0
for right, char in enumerate(answerKey):
# Count characters that are already the target
if char == target_char:
same_count += 1
# Window is valid when: (window_size - same_count) <= k
# Rearranged: same_count >= window_size - k
# Or: same_count > k when window_size = right - left + 1
if same_count > k:
if answerKey[left] == target_char:
same_count -= 1
left += 1
return len(answerKey) - left
2. Window Size Calculation Error
The Pitfall:
Returning right - left + 1
at the end instead of len(answerKey) - left
. The algorithm maintains the maximum window size found, and the final position of left
determines this maximum.
Incorrect:
max_length = 0
for right, char in enumerate(answerKey):
# ... sliding window logic ...
max_length = max(max_length, right - left + 1)
return max_length
Correct:
The window never shrinks below the maximum size found. When we need to slide, we move both pointers, maintaining the size. The final len(answerKey) - left
gives us the maximum window size achieved.
3. Misunderstanding the Problem Statement
The Pitfall:
Thinking you need to find consecutive characters in the original string, rather than understanding that you can change up to k
characters to create the consecutive sequence.
Example of Misunderstanding:
For "TFFT"
with k=1
, someone might think the answer is 2 (the existing "FF"), but actually it's 3 because you can change one 'T' to get "FFFT" or "TFFF".
Solution: Always remember: the goal is to find the longest substring where after making at most k changes, all characters become the same. The sliding window maintains a valid substring where the number of characters that need changing doesn't exceed k.
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
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
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Want a Structured Path to Master System Design Too? Don’t Miss This!