Given a binary string like "1001001", determine if the distance (gap) between every consecutive 1 is the same. This problem appears in pattern recognition, signal processing, and coding challenges.
Understanding the Problem
String: "1001001"
Positions: 0 3 6
Gaps: 3 3
All gaps equal → True (equidistant)String: "101001"
Positions: 0 2 5
Gaps: 2 3
Gaps differ → False (not equidistant)Using Index Collection
The standard approach:
- Find the index of every
1 - Calculate the differences between consecutive indices
- Check if all differences are equal
def has_equidistant_ones(s: str) -> bool:
"""Check if all 1s in a binary string are equidistant."""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return True
gaps = [indices[i + 1] - indices[i] for i in range(len(indices) - 1)]
return len(set(gaps)) == 1
print(has_equidistant_ones("1001001")) # True (gaps: 3, 3)
print(has_equidistant_ones("101001")) # False (gaps: 2, 3)
print(has_equidistant_ones("10101")) # True (gaps: 2, 2)
print(has_equidistant_ones("1")) # True (single 1)
print(has_equidistant_ones("0000")) # True (no 1s)Output:
True
False
True
True
TrueUsing zip for Cleaner Gap Calculation
def has_equidistant_ones_zip(s: str) -> bool:
"""Check equidistant 1s using zip for gap calculation."""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return True
gaps = [b - a for a, b in zip(indices, indices[1:])]
return len(set(gaps)) == 1Returning the Gap Value
Often you need to know the actual gap, not just whether they're equidistant:
def get_equidistant_gap(s: str) -> int | None:
"""
Return the gap if 1s are equidistant, None otherwise.
Returns 0 for strings with fewer than 2 ones.
"""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return 0
first_gap = indices[1] - indices[0]
for i in range(2, len(indices)):
if indices[i] - indices[i - 1] != first_gap:
return None
return first_gap
print(get_equidistant_gap("1001001")) # 3
print(get_equidistant_gap("101001")) # None
print(get_equidistant_gap("10101")) # 2Early Exit Optimization
For long strings, exit as soon as a mismatch is found:
def has_equidistant_ones_fast(s: str) -> bool:
"""Optimized version with early exit."""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return True
expected_gap = indices[1] - indices[0]
for i in range(2, len(indices)):
if indices[i] - indices[i - 1] != expected_gap:
return False
return TrueUsing NumPy for Large Data
When processing millions of bits, NumPy's vectorized operations are significantly faster:
import numpy as np
def has_equidistant_ones_numpy(s: str) -> bool:
"""Check equidistant 1s using NumPy (faster for large strings)."""
arr = np.array(list(s), dtype=np.int8)
indices = np.nonzero(arr)[0]
if len(indices) < 2:
return True
gaps = np.diff(indices)
return np.all(gaps == gaps[0])
print(has_equidistant_ones_numpy("1001001")) # TrueWorking with Integers Instead of Strings
If you have an integer and want to check its binary representation:
def has_equidistant_ones(s: str) -> bool:
"""Check if all 1s in a binary string are equidistant."""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return True
gaps = [indices[i + 1] - indices[i] for i in range(len(indices) - 1)]
return len(set(gaps)) == 1
def int_has_equidistant_ones(n: int) -> bool:
"""Check if 1s in binary representation of n are equidistant."""
if n <= 0:
return True
binary_str = bin(n)[2:] # Remove '0b' prefix
return has_equidistant_ones(binary_str)
print(int_has_equidistant_ones(73)) # 73 = 1001001 → True
print(int_has_equidistant_ones(41)) # 41 = 101001 → FalseOutput:
True
FalseFinding Equidistant Patterns
Find all substrings where 1s are equidistant:
def get_equidistant_gap(s: str) -> int | None:
"""
Return the gap if 1s are equidistant, None otherwise.
Returns 0 for strings with fewer than 2 ones.
"""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return 0
first_gap = indices[1] - indices[0]
for i in range(2, len(indices)):
if indices[i] - indices[i - 1] != first_gap:
return None
return first_gap
def has_equidistant_ones(s: str) -> bool:
"""Check if all 1s in a binary string are equidistant."""
indices = [i for i, char in enumerate(s) if char == '1']
if len(indices) < 2:
return True
gaps = [indices[i + 1] - indices[i] for i in range(len(indices) - 1)]
return len(set(gaps)) == 1
def find_equidistant_substrings(s: str, min_ones: int = 3) -> list:
"""Find all substrings with equidistant 1s."""
results = []
n = len(s)
for start in range(n):
for end in range(start + min_ones, n + 1):
substring = s[start:end]
ones_count = substring.count('1')
if ones_count >= min_ones and has_equidistant_ones(substring):
gap = get_equidistant_gap(substring)
results.append({
'substring': substring,
'start': start,
'end': end,
'ones': ones_count,
'gap': gap
})
return results
patterns = find_equidistant_substrings("10101001")
for p in patterns:
print(p)Output:
{'substring': '10101', 'start': 0, 'end': 5, 'ones': 3, 'gap': 2}
{'substring': '101010', 'start': 0, 'end': 6, 'ones': 3, 'gap': 2}
{'substring': '1010100', 'start': 0, 'end': 7, 'ones': 3, 'gap': 2}Edge Cases
| Input | Result | Reason |
|---|---|---|
"" |
True | No 1s to compare |
"0000" |
True | No 1s to compare |
"1" |
True | Single 1, no gap needed |
"11" |
True | Gap of 1 |
"1001001" |
True | Gaps: 3, 3 |
"101001" |
False | Gaps: 2, 3 |
Method Comparison
| Method | Time | Space | Best For |
|---|---|---|---|
| List + Set | O(n) | O(k) | General use |
| Early Exit | O(n) | O(k) | When false cases are common |
| NumPy | O(n) | O(n) | Large strings, batch processing |
n = string length, k = number of 1s
Summary
- Collect indices of all
1s using list comprehension withenumerate(). - Calculate gaps using
zip(indices, indices[1:])ornp.diff(). - Check uniformity with
len(set(gaps)) == 1or compare all gaps to the first. - Use early exit for better average-case performance.
- Consider NumPy only for very large datasets.