title: Check if Binary String Has at Most One Segment of Ones slug: check-if-binary-string-has-at-most-one-segment-of-ones difficulty: easy leetcode_id: 1784 leetcode_url: https://leetcode.com/problems/check-if-binary-string-has-at-most-one-segment-of-ones/ categories: - strings patterns: - two-pointers function_signature: "def check_ones_segment(s: str) -> bool:" test_cases: visible: - input: { s: "1001" } expected: false - input: { s: "110" } expected: true hidden: - input: { s: "1" } expected: true - input: { s: "10" } expected: true - input: { s: "11" } expected: true - input: { s: "111" } expected: true - input: { s: "1000" } expected: true - input: { s: "10001" } expected: false - input: { s: "1110" } expected: true description: | Given a binary string `s` **without leading zeros**, return `true` *if* `s` *contains **at most one contiguous segment of ones***. Otherwise, return `false`. A contiguous segment of ones means all the `1`s in the string appear consecutively without any `0`s between them. constraints: | - `1 <= s.length <= 100` - `s[i]` is either `'0'` or `'1'` - `s[0]` is `'1'` (no leading zeros) examples: - input: 's = "1001"' output: "false" explanation: "The ones do not form a contiguous segment. There is a '1' at index 0, then zeros, then another '1' at index 3." - input: 's = "110"' output: "true" explanation: "The ones form a single contiguous segment at the beginning: '11'. The trailing '0' doesn't break continuity." explanation: intuition: | The key insight comes from understanding what the constraint "no leading zeros" means for this problem. Since the string **must** start with a `'1'`, all the ones in a valid string must appear at the very beginning. Think of it like this: once we see a `'0'`, we've "left" the segment of ones. If we ever see another `'1'` after that, we've found a second segment — which makes the answer `false`. The critical observation is that a valid string looks like `1...10...0` (some ones followed by some zeros). An invalid string has the pattern `1...10...01...1` — where a `'1'` appears **after** a `'0'`. This simplifies to checking for the substring `"01"` followed by any `'1'`. Even simpler: if `"01"` appears anywhere in the string, there will be a `'1'` before the `'0'` (since the string starts with `'1'`), so we just need to check if `"01"` is followed eventually by another `'1'`. The simplest check: does `"01"` appear in the string? If yes, and since we start with `'1'`, the only way to have `"01"` is if there are ones, then zeros, and since the string started with ones, any character after `"01"` that is `'1'` creates two segments. Actually, the simplest observation: **if the pattern "01" appears in the string, check if any '1' comes after it**. But even simpler — since `s[0] = '1'`, we just need to check if the substring `"01"` appears in `s`. If it does, the string has the form `1...01...` and that trailing part could have more ones. Wait, let's reconsider. The string `"110"` contains `"01"` (at positions 1-2). But the answer is `true`. So just checking for `"01"` isn't enough. The real insight: we need to find if **after seeing a zero, we see another one**. This is equivalent to checking if the string contains `"01"` where there's a `'1'` somewhere after it. The simplest way: check if `"01"` appears in `s`. Since `s` has no leading zeros, `"01"` can only appear after some ones. After `"01"`, if there are more ones, we have two segments. If not (like `"110"`), we don't. The cleanest formulation: **the string is invalid if and only if it contains the substring `"01"` and any `'1'` appears after the first occurrence of `"01"`**. This is equivalent to checking if the string contains `"01"` at any position that isn't the very end, OR if it ends with `"01"` we're fine. The simplest check: **Does the string contain the pattern `"01"` where the `"1"` in `"01"` is followed by anything that contains a `'1'`?** This is just: `"01"` appears and isn't at the very end with nothing after, or what follows contains a `'1'`. Simplest of all: **Check if `"01"` appears in the string. If so, check if there's any `'1'` after that position.** This can be simplified to: **the string is invalid if and only if it contains the substring `"01"` AND the character after `"01"` (or later) is a `'1'`**. Actually the cleanest solution: **A string has two segments if and only if it contains `"01"` where a `'1'` appears anywhere after the `'0'` in `"01"`.** Since the string starts with `'1'`, this is equivalent to checking if the string contains `"01"` followed by any `'1'`. This is just: **does `"01"` appear anywhere except right before only zeros (or end of string)?** The trick is realising that the string format `1+0+` (one or more ones, followed by zero or more zeros) is the only valid format. Any `'1'` appearing after a `'0'` is invalid. So: **return `"01"` not in `s`**? No, `"110"` has `"01"` at the end and is valid! Final insight: Since the string cannot have leading zeros (starts with `'1'`), having one segment of ones means all ones are at the start. **The string is valid if and only if once we see a `'0'`, we never see a `'1'` again.** This is the pattern `"01"` followed by `'1'` — the substring `"01"` itself is okay, but not if followed by more ones. The simplest code: check if `"01"` appears anywhere. If the part after the `"01"` contains a `'1'`, return `false`. Since we only care about the first `"01"`, we can just check: **does the string contain the substring `"01"` where a `'1'` follows it?** This is equivalent to `"01"` being present AND not being the last two characters (since after them comes nothing or only zeros). Actually — just check if `'1'` appears after ANY `'0'`. If we ever see a `'0'` followed eventually by a `'1'`, that's two segments. This is precisely the check: **return `"01"` not in s** BUT we need `"01"` where the `'1'` is after the `'0'`. Wait, `"01"` is literally `'0'` followed by `'1'`. So the answer is: **return `"01"` not in s**. Let's verify: - `"1001"`: contains `"01"` at position 2-3? Actually position 2 is `'0'`, position 3 is `'1'`. Yes, `"01"` is at index 2. So return `false`. Correct! - `"110"`: contains `"01"` at position 1-2. So return `false`. But expected is `true`! So this approach is wrong. Let me re-examine. `"110"` has chars `'1'` at 0, `'1'` at 1, `'0'` at 2. So we have `"11"` then `"10"`. There's no `"01"` substring. So `"01" not in "110"` is `True`. Return `True`. Correct! Let me re-check `"1001"`: chars are `'1'`, `'0'`, `'0'`, `'1'`. The substrings of length 2 are `"10"`, `"00"`, `"01"`. Yes, `"01"` appears! So return `False`. Correct! The solution is simply: **return `"01"` not in s**. approach: | We solve this using a **Substring Check** approach: **Step 1: Understand the pattern** - A binary string with no leading zeros starts with `'1'` - For all ones to be contiguous, they must all be at the beginning - The valid pattern is: `1...10...0` (ones followed by zeros) - The invalid pattern is: `1...10...01` (a `'1'` appearing after a `'0'`)   **Step 2: Identify the key insight** - If a `'0'` is ever followed directly by a `'1'` (the substring `"01"`), there are multiple segments - If `"01"` never appears, all ones are contiguous at the start   **Step 3: Check for the pattern** - Simply check if the substring `"01"` exists in `s` - If it does, return `false` (multiple segments) - If it doesn't, return `true` (at most one segment)   This works because `"01"` appearing means we transitioned from zeros back to ones, which is only possible if there's a gap in the ones. common_pitfalls: - title: Counting Segments with State Machine description: | A common over-complication is to build a state machine or counter to track "entering" and "leaving" segments of ones. While this works, it's more complex than necessary. The substring check `"01" in s` captures the exact condition in one operation. wrong_approach: "Tracking state transitions and counting segments" correct_approach: "Check for '01' substring" - title: Misunderstanding the Leading Zero Constraint description: | Some solutions forget that `s[0]` is guaranteed to be `'1'`. This constraint is crucial — it means we don't need to handle cases like `"0110"`. Without this constraint, we'd need additional logic. With it, any `"01"` substring definitively indicates multiple segments. wrong_approach: "Adding unnecessary checks for leading zeros" correct_approach: "Trust the constraint that s[0] = '1'" - title: Off-by-One in Manual Iteration description: | When manually iterating, it's easy to make off-by-one errors when checking adjacent characters. ```python # Wrong: might miss the last pair for i in range(len(s) - 1): # Correct, but easy to write range(len(s)) by mistake ``` Using the built-in substring check avoids these issues entirely. key_takeaways: - "**Pattern recognition**: Translate the problem into a simple substring or pattern check when possible" - "**Use constraints**: The 'no leading zeros' constraint simplifies the problem significantly" - "**Built-in operations**: Python's `in` operator for substrings is both readable and efficient" - "**Similar problems**: This pattern of checking for forbidden substrings applies to many string validation problems" time_complexity: "O(n). The substring check `\"01\" in s` scans the string once." space_complexity: "O(1). We only perform a containment check without allocating additional data structures." solutions: - approach_name: Substring Check is_optimal: true code: | def check_ones_segment(s: str) -> bool: # If '01' appears, a '1' comes after a '0' — meaning multiple segments # If '01' doesn't appear, all '1's are contiguous at the start return "01" not in s explanation: | **Time Complexity:** O(n) — Single pass through the string for substring search. **Space Complexity:** O(1) — No additional space used. This elegant one-liner leverages the constraint that the string has no leading zeros. Since the string starts with `'1'`, the only way to have the substring `"01"` is if there's a `'0'` followed by a `'1'` — which means the ones are not contiguous. - approach_name: Linear Scan with Flag is_optimal: false code: | def check_ones_segment(s: str) -> bool: seen_zero = False for char in s: if char == '0': # We've left the initial segment of ones seen_zero = True elif seen_zero: # Found a '1' after seeing a '0' — two segments return False # Never found a '1' after a '0' return True explanation: | **Time Complexity:** O(n) — Single pass through the string. **Space Complexity:** O(1) — Only one boolean flag used. This approach explicitly tracks whether we've seen a `'0'`. Once we have, any subsequent `'1'` means there are multiple segments. While correct and efficient, the substring check approach is more concise.