2839. Check if Strings Can be Made Equal With Operations I
Problem Description
You have two strings s1
and s2
, each containing exactly 4 lowercase English letters.
You can perform a specific swap operation on either string as many times as you want:
- Pick two indices
i
andj
wherej - i = 2
(meaning the indices are exactly 2 positions apart) - Swap the characters at positions
i
andj
in the string
For example, in a string of length 4 with indices 0, 1, 2, 3:
- You can swap characters at indices 0 and 2
- You can swap characters at indices 1 and 3
Your task is to determine if it's possible to make s1
equal to s2
by applying this swap operation any number of times to either string. Return true
if you can make them equal, false
otherwise.
The key insight is that this operation only allows swapping between positions that are 2 apart. This means:
- Characters at even positions (0, 2) can only be swapped with each other
- Characters at odd positions (1, 3) can only be swapped with each other
- Characters at even positions can never move to odd positions and vice versa
Therefore, for the strings to be made equal, the characters at even positions in s1
must match (in some order) the characters at even positions in s2
, and similarly for odd positions.
Intuition
Let's think about what the operation actually allows us to do. When we can only swap indices that are exactly 2 positions apart, we need to identify which positions can interact with each other.
In a string of length 4 with indices [0, 1, 2, 3]
:
- Index 0 can swap with index 2
- Index 1 can swap with index 3
- That's it - no other swaps are possible
This creates two independent groups:
- Group 1: positions 0 and 2 (even indices)
- Group 2: positions 1 and 3 (odd indices)
Characters within the same group can be rearranged freely through swapping, but characters can never move between groups. A character at position 0 can move to position 2 and vice versa, but it can never reach positions 1 or 3.
This means the problem reduces to checking if:
- The characters at even positions in
s1
(positions 0 and 2) contain the same set of characters as the even positions ins2
- The characters at odd positions in
s1
(positions 1 and 3) contain the same set of characters as the odd positions ins2
If both conditions are met, we can rearrange the characters within each group to match the target string. If either condition fails, it's impossible to make the strings equal.
For example:
s1 = "abcd"
ands2 = "cdab"
- Even positions:
s1[0,2] = "ac"
,s2[0,2] = "ca"
→ same characters ✓ - Odd positions:
s1[1,3] = "bd"
,s2[1,3] = "db"
→ same characters ✓ - Result: Can be made equal
The solution simply sorts the characters at even and odd positions separately for both strings and compares them. If they match, the transformation is possible.
Solution Approach
Based on our intuition that characters at even and odd positions form independent groups, we can implement the solution using a simple counting or sorting approach.
The implementation leverages Python's string slicing to separate characters by position parity:
s1[::2]
extracts characters at even indices (0, 2)s1[1::2]
extracts characters at odd indices (1, 3)
Algorithm Steps:
-
Extract even-indexed characters from both strings:
- For
s1
: Get characters at positions 0 and 2 usings1[::2]
- For
s2
: Get characters at positions 0 and 2 usings2[::2]
- For
-
Extract odd-indexed characters from both strings:
- For
s1
: Get characters at positions 1 and 3 usings1[1::2]
- For
s2
: Get characters at positions 1 and 3 usings2[1::2]
- For
-
Sort and compare the extracted characters:
- Sort the even-indexed characters from both strings and check if they're equal
- Sort the odd-indexed characters from both strings and check if they're equal
-
Return the result:
- Return
True
if both comparisons are equal - Return
False
otherwise
- Return
Why sorting works: Sorting gives us a canonical representation of the character set at each position group. If two sorted sequences are equal, it means they contain the same characters with the same frequencies, which is exactly what we need to verify.
Time Complexity: O(n + |Σ|)
where n
is the length of the string (4 in this case) and |Σ|
is the size of the character set. Since the string length is fixed at 4, this is effectively O(1)
.
Space Complexity: O(|Σ|)
for storing the sorted characters, which is also effectively O(1)
given the fixed string length.
The solution elegantly handles the problem by recognizing that the swap operation partitions the string indices into two independent sets, reducing the problem to a simple character frequency comparison within each set.
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 s1 = "abdc"
and s2 = "cbad"
.
Step 1: Identify what swaps are possible
- In position indices [0, 1, 2, 3]:
- We can swap index 0 with 2 (distance = 2)
- We can swap index 1 with 3 (distance = 2)
Step 2: Extract characters by position groups
For s1 = "abdc"
:
- Even positions (0, 2):
s1[0] = 'a'
,s1[2] = 'd'
→ "ad" - Odd positions (1, 3):
s1[1] = 'b'
,s1[3] = 'c'
→ "bc"
For s2 = "cbad"
:
- Even positions (0, 2):
s2[0] = 'c'
,s2[2] = 'a'
→ "ca" - Odd positions (1, 3):
s2[1] = 'b'
,s2[3] = 'd'
→ "bd"
Step 3: Sort each group and compare
Even positions:
s1
even sorted: "ad" → "ad"s2
even sorted: "ca" → "ac"- Compare: "ad" ≠ "ac" ❌
Since the even positions don't match, we can already conclude it's impossible to transform s1
into s2
. The character 'd' at an even position in s1
cannot be moved to an odd position, and there's no 'd' at any even position in s2
.
Result: Return false
Let's verify with a positive example: s1 = "abcd"
and s2 = "cdab"
Even positions:
s1
: positions 0,2 → "ac"s2
: positions 0,2 → "ca"- Sorted: "ac" = "ac" ✓
Odd positions:
s1
: positions 1,3 → "bd"s2
: positions 1,3 → "db"- Sorted: "bd" = "bd" ✓
Both groups match, so we can transform s1
to s2
through swaps:
- Start with "abcd"
- Swap positions 0 and 2: "cbad"
- Swap positions 1 and 3: "cdab" ✓
Result: Return true
Solution Implementation
1class Solution:
2 def canBeEqual(self, s1: str, s2: str) -> bool:
3 """
4 Check if two strings can be made equal by swapping characters at even or odd positions.
5
6 The key insight is that characters at even indices can only be swapped with other
7 characters at even indices, and characters at odd indices can only be swapped with
8 other characters at odd indices.
9
10 Args:
11 s1: First string to compare
12 s2: Second string to compare
13
14 Returns:
15 True if the strings can be made equal through allowed swaps, False otherwise
16 """
17 # Extract and sort characters at even positions (0, 2, 4, ...) from both strings
18 s1_even_chars_sorted = sorted(s1[::2])
19 s2_even_chars_sorted = sorted(s2[::2])
20
21 # Extract and sort characters at odd positions (1, 3, 5, ...) from both strings
22 s1_odd_chars_sorted = sorted(s1[1::2])
23 s2_odd_chars_sorted = sorted(s2[1::2])
24
25 # Both even and odd position characters must match after sorting
26 return (s1_even_chars_sorted == s2_even_chars_sorted and
27 s1_odd_chars_sorted == s2_odd_chars_sorted)
28
1class Solution {
2 public boolean canBeEqual(String s1, String s2) {
3 // Create a 2D array to track character frequency differences
4 // First dimension: [0] for even positions, [1] for odd positions
5 // Second dimension: 26 slots for each lowercase letter (a-z)
6 int[][] characterCountDifference = new int[2][26];
7
8 // Process each character position in both strings
9 for (int position = 0; position < s1.length(); position++) {
10 // Determine if position is even (0) or odd (1) using bitwise AND
11 int parityIndex = position & 1;
12
13 // Get the character index (0-25) for the current character in s1
14 int s1CharIndex = s1.charAt(position) - 'a';
15 // Increment count for this character at this parity position
16 characterCountDifference[parityIndex][s1CharIndex]++;
17
18 // Get the character index (0-25) for the current character in s2
19 int s2CharIndex = s2.charAt(position) - 'a';
20 // Decrement count for this character at this parity position
21 characterCountDifference[parityIndex][s2CharIndex]--;
22 }
23
24 // Check if all character counts are balanced (equal to zero)
25 // This means each parity position has the same characters in both strings
26 for (int charIndex = 0; charIndex < 26; charIndex++) {
27 // Check even position counts
28 if (characterCountDifference[0][charIndex] != 0) {
29 return false;
30 }
31 // Check odd position counts
32 if (characterCountDifference[1][charIndex] != 0) {
33 return false;
34 }
35 }
36
37 // All character counts are balanced, strings can be made equal
38 return true;
39 }
40}
41
1class Solution {
2public:
3 bool canBeEqual(string s1, string s2) {
4 // Create a 2D array to track character frequency differences
5 // charFrequency[0] tracks characters at even positions (0, 2, 4, ...)
6 // charFrequency[1] tracks characters at odd positions (1, 3, 5, ...)
7 vector<vector<int>> charFrequency(2, vector<int>(26, 0));
8
9 // Process each character in both strings
10 for (int i = 0; i < s1.size(); ++i) {
11 int positionParity = i & 1; // 0 for even indices, 1 for odd indices
12
13 // Increment count for character from s1 at this position parity
14 ++charFrequency[positionParity][s1[i] - 'a'];
15
16 // Decrement count for character from s2 at this position parity
17 --charFrequency[positionParity][s2[i] - 'a'];
18 }
19
20 // Check if all character frequencies are balanced (should be 0)
21 // If any frequency is non-zero, strings cannot be made equal
22 for (int charIndex = 0; charIndex < 26; ++charIndex) {
23 if (charFrequency[0][charIndex] != 0 || charFrequency[1][charIndex] != 0) {
24 return false;
25 }
26 }
27
28 // All frequencies are balanced, strings can be made equal
29 return true;
30 }
31};
32
1/**
2 * Checks if two strings can be made equal by swapping characters at even or odd positions
3 * @param s1 - First string to compare
4 * @param s2 - Second string to compare
5 * @returns true if strings can be made equal, false otherwise
6 */
7function canBeEqual(s1: string, s2: string): boolean {
8 // Create a 2x26 array to track character frequency differences
9 // Row 0: characters at even positions, Row 1: characters at odd positions
10 // Each column represents a letter (a-z)
11 const characterCountDifference: number[][] = Array.from(
12 { length: 2 },
13 () => Array.from({ length: 26 }, () => 0)
14 );
15
16 // Iterate through both strings simultaneously
17 for (let i = 0; i < s1.length; i++) {
18 // Determine if current position is even (0) or odd (1)
19 const positionParity = i & 1;
20
21 // Get character codes and normalize to 0-25 range (a=0, b=1, ..., z=25)
22 const s1CharIndex = s1.charCodeAt(i) - 97;
23 const s2CharIndex = s2.charCodeAt(i) - 97;
24
25 // Increment count for s1 character at this position parity
26 characterCountDifference[positionParity][s1CharIndex]++;
27
28 // Decrement count for s2 character at this position parity
29 characterCountDifference[positionParity][s2CharIndex]--;
30 }
31
32 // Check if all character counts are balanced (equal to 0)
33 // If any count is non-zero, strings cannot be made equal
34 for (let charIndex = 0; charIndex < 26; charIndex++) {
35 if (characterCountDifference[0][charIndex] !== 0 ||
36 characterCountDifference[1][charIndex] !== 0) {
37 return false;
38 }
39 }
40
41 // All character counts are balanced, strings can be made equal
42 return true;
43}
44
Time and Space Complexity
Time Complexity: O(n log n)
where n
is the length of the input strings.
The code performs the following operations:
s1[::2]
ands2[::2]
extract characters at even indices -O(n/2)
eachs1[1::2]
ands2[1::2]
extract characters at odd indices -O(n/2)
eachsorted()
is called 4 times on strings of lengthn/2
- each sorting takesO((n/2) log(n/2))
- Total sorting time:
4 * O((n/2) log(n/2)) = O(n log n)
- The equality comparisons take
O(n/2)
each
Overall time complexity is dominated by the sorting operations: O(n log n)
Space Complexity: O(n)
The space usage includes:
- String slicing creates 4 new strings of length
n/2
each -O(2n) = O(n)
- The
sorted()
function creates 4 new sorted lists/strings of lengthn/2
each -O(2n) = O(n)
- The sorting algorithm itself uses
O(n/2)
auxiliary space for each call
Total space complexity: O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Misunderstanding the Swap Operation Range
The Mistake: Developers often misinterpret "j - i = 2" as allowing any swap where indices differ by 2, leading to incorrect implementations like:
# WRONG: This assumes we can swap any positions 2 apart in any string length
def canBeEqual(self, s1: str, s2: str) -> bool:
# Might try to generalize for any string length
for i in range(len(s1) - 2):
# Attempts to swap positions i and i+2
pass
Why It's Wrong: The problem specifically states the strings have exactly 4 characters. The constraint "j - i = 2" with a 4-character string means:
- Only positions (0,2) can be swapped together
- Only positions (1,3) can be swapped together
- This creates two independent groups that never mix
The Fix: Recognize that for 4-character strings, the positions form two fixed groups:
# Correct understanding: positions are partitioned into [0,2] and [1,3] even_positions = s1[::2] # Gets characters at indices 0, 2 odd_positions = s1[1::2] # Gets characters at indices 1, 3
Pitfall 2: Attempting Direct String Manipulation
The Mistake: Some developers try to actually perform the swaps to transform one string into another:
# WRONG: Trying to simulate the actual swapping process
def canBeEqual(self, s1: str, s2: str) -> bool:
s1_list = list(s1)
# Try swapping positions 0 and 2
s1_list[0], s1_list[2] = s1_list[2], s1_list[0]
if ''.join(s1_list) == s2:
return True
# Try swapping positions 1 and 3
s1_list[1], s1_list[3] = s1_list[3], s1_list[1]
# ... more swap attempts
Why It's Wrong: This approach:
- Only checks specific swap sequences, not all possible combinations
- Becomes exponentially complex as you need to check all possible swap combinations
- Misses the mathematical insight that only character frequencies matter within each group
The Fix: Use sorting or counting to compare character sets instead of simulating swaps:
# Correct: Compare sorted character groups
return (sorted(s1[::2]) == sorted(s2[::2]) and
sorted(s1[1::2]) == sorted(s2[1::2]))
Pitfall 3: Overlooking the Independence of Position Groups
The Mistake: Checking if the entire strings contain the same characters without considering position constraints:
# WRONG: Just checking if strings are anagrams
def canBeEqual(self, s1: str, s2: str) -> bool:
return sorted(s1) == sorted(s2)
Why It's Wrong: This ignores the fundamental constraint that characters at even positions can never move to odd positions and vice versa. For example:
- s1 = "abcd" and s2 = "badc"
- These are anagrams, but cannot be made equal through allowed swaps
- 'a' is at position 0 (even) in s1 but at position 1 (odd) in s2
The Fix: Always separate and compare the position groups independently:
# Correct: Check even and odd positions separately
even_match = sorted(s1[::2]) == sorted(s2[::2])
odd_match = sorted(s1[1::2]) == sorted(s2[1::2])
return even_match and odd_match
Which of these properties could exist for a graph but not a tree?
Recommended Readings
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!