Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1433. Check If a String Can Break Another String - Leetcode Solution

Problem Description

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.

  • Both strings are the same length.
  • All characters are lowercase English letters.
  • Each character can only be used once per string.

Thought Process

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.

Solution Approach

  • Step 1: Sort both s1 and s2 in ascending order. This gives us the "weakest" arrangement for each string.
  • Step 2: Compare the sorted strings character by character from left to right.
  • Step 3: Check two possibilities:
    • For every index i, s1[i] ≥ s2[i]. If this is true for all indices, s1 can break s2.
    • For every index i, s2[i] ≥ s1[i]. If this is true for all indices, s2 can break s1.
  • Step 4: Return 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).

Example Walkthrough

Let's walk through an example:
s1 = "abc", s2 = "xya"

  1. Sort both strings:
    • s1_sorted = "abc"
    • s2_sorted = "axy"
  2. Compare s1_sorted with s2_sorted:
    • At index 0: 'a' < 'a' (False, but equal is OK)
    • At index 1: 'b' < 'x' (False)
    • At index 2: 'c' < 'y' (False)
    So s1 cannot break s2.
  3. Compare s2_sorted with s1_sorted:
    • At index 0: 'a' >= 'a' (True)
    • At index 1: 'x' >= 'b' (True)
    • At index 2: 'y' >= 'c' (True)
    All are True, so s2 can break s1.
  4. Return True since at least one string can break the other.

Code Implementation

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;
};
      

Time and Space Complexity

  • Brute-force Approach:
    • Would involve generating all permutations of both strings and comparing them. This is O(n! \times n!) and is computationally infeasible for even modest string lengths.
  • Optimized Approach (Sorting):
    • Sorting both strings: O(n \log n) each, so total O(n \log n).
    • Comparing characters: O(n).
    • Total Time Complexity: O(n \log n)
    • Space Complexity: O(n) for storing sorted arrays (or strings).

Summary

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.