Given two strings, s1
and s2
, both of the same length and containing only lowercase English letters, determine if one string can "break" the other.
A string x
can "break" string y
if, after rearranging both strings, for every position i
(where 0 ≤ i < n
and n
is the length of the strings), the character at x[i]
is greater than or equal to y[i]
(using standard lexicographical order).
Your task is to return True
if either s1
can break s2
or s2
can break s1
. Otherwise, return False
.
At first, it might seem we need to try all possible rearrangements of the two strings and compare them, which would be computationally expensive. But if we think about the problem, we realize that the order of the characters in the rearrangement doesn't matter as long as the comparison for every position holds.
This leads to the insight that sorting both strings puts their smallest letters first, and so the "weakest" possible arrangement for each string is when both are sorted in ascending order. If, after sorting, one string is always greater than or equal to the other at every position, then it will be able to "break" the other in any arrangement.
Therefore, instead of checking all permutations, we can just sort both strings and do a simple character-by-character comparison.
s1
and s2
in ascending order. This gives us the "weakest" arrangement for each string.i
, s1[i] ≥ s2[i]
. If this is true for all indices, s1
can break s2
.i
, s2[i] ≥ s1[i]
. If this is true for all indices, s2
can break s1
.True
if either possibility holds; otherwise, return False
.
This approach is efficient because sorting is O(n \log n)
and the comparison is O(n)
.
Let's walk through an example:
s1 = "abc"
, s2 = "xya"
s1_sorted = "abc"
s2_sorted = "axy"
s1_sorted
with s2_sorted
:
'a' < 'a'
(False, but equal is OK)'b' < 'x'
(False)'c' < 'y'
(False)s1
cannot break s2
.
s2_sorted
with s1_sorted
:
'a' >= 'a'
(True)'x' >= 'b'
(True)'y' >= 'c'
(True)s2
can break s1
.
True
since at least one string can break the other.class Solution:
def checkIfCanBreak(self, s1: str, s2: str) -> bool:
s1_sorted = sorted(s1)
s2_sorted = sorted(s2)
can_s1_break = all(a >= b for a, b in zip(s1_sorted, s2_sorted))
can_s2_break = all(b >= a for a, b in zip(s1_sorted, s2_sorted))
return can_s1_break or can_s2_break
class Solution {
public:
bool checkIfCanBreak(string s1, string s2) {
sort(s1.begin(), s1.end());
sort(s2.begin(), s2.end());
bool can_s1_break = true, can_s2_break = true;
for (int i = 0; i < s1.length(); ++i) {
if (s1[i] < s2[i]) can_s1_break = false;
if (s2[i] < s1[i]) can_s2_break = false;
}
return can_s1_break || can_s2_break;
}
};
import java.util.Arrays;
class Solution {
public boolean checkIfCanBreak(String s1, String s2) {
char[] arr1 = s1.toCharArray();
char[] arr2 = s2.toCharArray();
Arrays.sort(arr1);
Arrays.sort(arr2);
boolean canS1Break = true, canS2Break = true;
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] < arr2[i]) canS1Break = false;
if (arr2[i] < arr1[i]) canS2Break = false;
}
return canS1Break || canS2Break;
}
}
var checkIfCanBreak = function(s1, s2) {
let arr1 = s1.split('').sort();
let arr2 = s2.split('').sort();
let canS1Break = true, canS2Break = true;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] < arr2[i]) canS1Break = false;
if (arr2[i] < arr1[i]) canS2Break = false;
}
return canS1Break || canS2Break;
};
O(n! \times n!)
and is computationally infeasible for even modest string lengths.O(n \log n)
each, so total O(n \log n)
.O(n)
.O(n \log n)
O(n)
for storing sorted arrays (or strings).The key insight for this problem is that sorting both strings gives us the "weakest" possible arrangement for each, allowing a direct comparison to check if one can "break" the other. This avoids the need for expensive brute-force permutation checks. The solution is elegant, efficient, and easy to implement, relying on basic sorting and array comparison.