159 lines
7.6 KiB
YAML
159 lines
7.6 KiB
YAML
title: Apply Bitwise Operations to Make Strings Equal
|
|
slug: apply-bitwise-operations-to-make-strings-equal
|
|
difficulty: medium
|
|
leetcode_id: 2546
|
|
leetcode_url: https://leetcode.com/problems/apply-bitwise-operations-to-make-strings-equal/
|
|
categories:
|
|
- strings
|
|
- math
|
|
patterns:
|
|
- greedy
|
|
|
|
description: |
|
|
You are given two **0-indexed binary** strings `s` and `target` of the same length `n`. You can do the following operation on `s` **any** number of times:
|
|
|
|
- Choose two **different** indices `i` and `j` where `0 <= i, j < n`.
|
|
- Simultaneously, replace `s[i]` with (`s[i]` **OR** `s[j]`) and `s[j]` with (`s[i]` **XOR** `s[j]`).
|
|
|
|
For example, if `s = "0110"`, you can choose `i = 0` and `j = 2`, then simultaneously replace `s[0]` with (`s[0]` **OR** `s[2]` = `0` **OR** `1` = `1`), and `s[2]` with (`s[0]` **XOR** `s[2]` = `0` **XOR** `1` = `1`). So we will have `s = "1110"`.
|
|
|
|
Return `true` *if you can make the string* `s` *equal to* `target`*, or* `false` *otherwise*.
|
|
|
|
constraints: |
|
|
- `n == s.length == target.length`
|
|
- `2 <= n <= 10^5`
|
|
- `s` and `target` consist of only the digits `0` and `1`.
|
|
|
|
examples:
|
|
- input: 's = "1010", target = "0110"'
|
|
output: "true"
|
|
explanation: "We can do the following operations: Choose i = 2 and j = 0, giving s = \"0010\". Then choose i = 2 and j = 1, giving s = \"0110\". Since we can make s equal to target, we return true."
|
|
- input: 's = "11", target = "00"'
|
|
output: "false"
|
|
explanation: "It is not possible to make s equal to target with any number of operations."
|
|
|
|
explanation:
|
|
intuition: |
|
|
This problem seems complex at first — we need to understand what the operation actually does before finding the pattern.
|
|
|
|
Let's analyse all possible cases when we apply the operation to positions with values `(a, b)`:
|
|
|
|
- `(0, 0)` → `(0 OR 0, 0 XOR 0)` = `(0, 0)` — no change
|
|
- `(0, 1)` → `(0 OR 1, 0 XOR 1)` = `(1, 1)` — the `1` "spreads" to position `i`
|
|
- `(1, 0)` → `(1 OR 0, 1 XOR 0)` = `(1, 1)` — the `1` "spreads" to position `j`
|
|
- `(1, 1)` → `(1 OR 1, 1 XOR 1)` = `(1, 0)` — one `1` becomes `0`
|
|
|
|
The key insight emerges: **as long as there is at least one `1` in the string, we can move and manipulate `1`s freely**. We can duplicate a `1` to any position (spread), and we can eliminate a `1` at any position (by using two `1`s to make one of them `0`).
|
|
|
|
However, we **cannot create a `1` from nothing**. If `s` has no `1`s, it stays all zeros. Similarly, we **cannot eliminate all `1`s** — if we have any `1`, we'll always have at least one.
|
|
|
|
This leads to a simple rule: both strings must either both contain at least one `1`, or both be all zeros.
|
|
|
|
approach: |
|
|
The solution becomes remarkably simple once we understand the invariant:
|
|
|
|
**Step 1: Check if `s` contains at least one `1`**
|
|
|
|
- Count or check for presence of `'1'` in string `s`
|
|
- This tells us if we have any "fuel" to work with
|
|
|
|
|
|
|
|
**Step 2: Check if `target` contains at least one `1`**
|
|
|
|
- Similarly, check for presence of `'1'` in `target`
|
|
- This tells us what state we need to achieve
|
|
|
|
|
|
|
|
**Step 3: Compare the two conditions**
|
|
|
|
- If both have at least one `1` → return `true` (we can transform freely)
|
|
- If both have no `1`s (all zeros) → return `true` (already equal or trivially transformable)
|
|
- If one has a `1` and the other doesn't → return `false` (impossible)
|
|
|
|
|
|
|
|
In other words: return `true` if and only if `('1' in s) == ('1' in target)`.
|
|
|
|
common_pitfalls:
|
|
- title: Trying to Simulate the Operations
|
|
description: |
|
|
A natural instinct is to try simulating the operations step by step to see if we can reach the target. This approach is fundamentally flawed:
|
|
|
|
- There's no clear termination condition
|
|
- The search space is exponential
|
|
- We might loop forever without finding the answer
|
|
|
|
The key is to find an **invariant** — a property that remains unchanged regardless of operations. Here, the invariant is "has at least one `1`".
|
|
wrong_approach: "BFS/DFS simulation of all possible operations"
|
|
correct_approach: "Check if both strings have the same 'has at least one 1' property"
|
|
|
|
- title: Counting Exact Number of 1s
|
|
description: |
|
|
You might think the number of `1`s needs to match between `s` and `target`. This is incorrect!
|
|
|
|
Consider `s = "10"` and `target = "11"`:
|
|
- Apply operation with `i=0, j=1`: `(1, 0)` → `(1, 1)`
|
|
- We went from one `1` to two `1`s
|
|
|
|
Consider `s = "11"` and `target = "10"`:
|
|
- Apply operation with `i=0, j=1`: `(1, 1)` → `(1, 0)`
|
|
- We went from two `1`s to one `1`
|
|
|
|
The count can change; only the **presence** of at least one `1` matters.
|
|
wrong_approach: "Check if s.count('1') == target.count('1')"
|
|
correct_approach: "Check if ('1' in s) == ('1' in target)"
|
|
|
|
- title: Missing the All-Zeros Edge Case
|
|
description: |
|
|
If both strings are `"000...0"` (all zeros), the answer is `true` — they're already equal, or can be trivially transformed (applying any operation on all zeros changes nothing).
|
|
|
|
Make sure your condition handles this: both having zero `1`s should return `true`.
|
|
|
|
key_takeaways:
|
|
- "**Find the invariant**: For transformation problems, identify what property stays constant across all operations"
|
|
- "**Bit manipulation insight**: OR spreads `1`s, XOR toggles — together they allow free movement of `1`s but can't create them from nothing"
|
|
- "**Simplification**: Complex-looking problems often reduce to simple checks once you understand the underlying mechanics"
|
|
- "**Presence vs count**: Sometimes the *existence* of something matters more than the *quantity*"
|
|
|
|
time_complexity: "O(n). We scan each string once to check for the presence of `'1'`."
|
|
space_complexity: "O(1). We only use a constant amount of extra space for the boolean checks."
|
|
|
|
solutions:
|
|
- approach_name: Invariant Check
|
|
is_optimal: true
|
|
code: |
|
|
def make_strings_equal(s: str, target: str) -> bool:
|
|
# Check if both strings have the same "has at least one 1" property
|
|
# If both have a 1, we can freely transform between any configurations
|
|
# If neither has a 1, they're both all zeros (equal)
|
|
# If only one has a 1, transformation is impossible
|
|
has_one_s = '1' in s
|
|
has_one_target = '1' in target
|
|
|
|
return has_one_s == has_one_target
|
|
explanation: |
|
|
**Time Complexity:** O(n) — We check for '1' in each string, which scans at most n characters each.
|
|
|
|
**Space Complexity:** O(1) — Only two boolean variables used.
|
|
|
|
The solution leverages the key insight that the operation preserves the "has at least one 1" invariant. We can spread 1s or eliminate them (by converting 1,1 to 1,0), but we can never go from "no 1s" to "some 1s" or vice versa.
|
|
|
|
- approach_name: Count-Based Check
|
|
is_optimal: true
|
|
code: |
|
|
def make_strings_equal(s: str, target: str) -> bool:
|
|
# Alternative: check if counts are both zero or both non-zero
|
|
count_s = s.count('1')
|
|
count_target = target.count('1')
|
|
|
|
# Both must be zero, or both must be positive
|
|
return (count_s == 0) == (count_target == 0)
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Counting '1's requires scanning the full string.
|
|
|
|
**Space Complexity:** O(1) — Only integer counters used.
|
|
|
|
This approach counts the 1s explicitly, then checks if both counts are zero or both are positive. It's equivalent to the first solution but uses count instead of membership check. The `in` operator short-circuits on first match, so the first solution may be slightly faster in practice.
|