title: Ambiguous Coordinates
slug: ambiguous-coordinates
difficulty: medium
leetcode_id: 816
leetcode_url: https://leetcode.com/problems/ambiguous-coordinates/
categories:
  - strings
  - recursion
patterns:
  - slug: backtracking
    is_optimal: true

function_signature: "def ambiguous_coordinates(s: str) -> list[str]:"

test_cases:
  visible:
    - input: { s: "(123)" }
      expected: ["(1, 2.3)", "(1, 23)", "(1.2, 3)", "(12, 3)"]
    - input: { s: "(0123)" }
      expected: ["(0, 1.23)", "(0, 12.3)", "(0, 123)", "(0.1, 2.3)", "(0.1, 23)", "(0.12, 3)"]
  hidden:
    - input: { s: "(00011)" }
      expected: ["(0, 0.011)", "(0.001, 1)"]
    - input: { s: "(100)" }
      expected: ["(10, 0)", "(1, 0.0)"]
    - input: { s: "(12)" }
      expected: ["(1, 2)"]

description: |
  We had some 2-dimensional coordinates, like `"(1, 3)"` or `"(2, 0.5)"`. Then, we removed all commas, decimal points, and spaces and ended up with the string `s`.

  For example, `"(1, 3)"` becomes `s = "(13)"` and `"(2, 0.5)"` becomes `s = "(205)"`.

  Return *a list of strings representing all possibilities for what our original coordinates could have been*.

  Our original representation never had extraneous zeroes, so we never started with numbers like `"00"`, `"0.0"`, `"0.00"`, `"1.0"`, `"001"`, `"00.01"`, or any other number that can be represented with fewer digits. Also, a decimal point within a number never occurs without at least one digit occurring before it, so we never started with numbers like `".1"`.

  The final answer list can be returned in any order. All coordinates in the final answer have exactly one space between them (occurring after the comma).

constraints: |
  - `4 <= s.length <= 12`
  - `s[0] == '('` and `s[s.length - 1] == ')'`
  - The rest of `s` are digits

examples:
  - input: 's = "(123)"'
    output: '["(1, 2.3)","(1, 23)","(1.2, 3)","(12, 3)"]'
    explanation: "We can split the digits '123' into x and y coordinates in multiple ways, and each coordinate can optionally have a decimal point."
  - input: 's = "(0123)"'
    output: '["(0, 1.23)","(0, 12.3)","(0, 123)","(0.1, 2.3)","(0.1, 23)","(0.12, 3)"]'
    explanation: "0.0, 00, 0001, or 00.01 are not allowed due to extraneous zeros."
  - input: 's = "(00011)"'
    output: '["(0, 0.011)","(0.001, 1)"]'
    explanation: "Leading zeros severely limit valid representations. '00' as an integer or '00.x' as a decimal are invalid."

explanation:
  intuition: |
    Imagine you're trying to decode a compressed message. Someone took a coordinate like `(1.5, 2.3)` and stripped away all the formatting — the comma, spaces, and decimal points — leaving just `(1523)`. Your job is to figure out all the ways this could be "unpacked" back into valid coordinates.

    The key insight is to break this into **two independent subproblems**:

    1. **Split the string** into two parts: one for the x-coordinate and one for the y-coordinate
    2. **Generate all valid numbers** from each part by optionally inserting a decimal point

    For each split position, you independently generate all valid representations for the left part (x) and the right part (y), then combine them. This is essentially a **Cartesian product** of possibilities.

    The tricky part is knowing which number representations are *valid*. A number is invalid if it has:
    - Leading zeros (like `"01"` or `"001"`) — unless it's just `"0"`
    - Trailing zeros after a decimal point (like `"1.0"` or `"2.30"`)

    Think of it like reconstructing a puzzle where you try every valid piece combination systematically.

  approach: |
    We solve this using **systematic enumeration** with careful validation:

    **Step 1: Extract the digit string**

    - Remove the parentheses from `s` to get the raw digits
    - Example: `"(123)"` → `"123"`

    &nbsp;

    **Step 2: Split into two coordinate parts**

    - Try every possible split point: left part gets 1 to n-1 digits, right part gets the rest
    - For `"123"`: splits are `("1", "23")`, `("12", "3")`
    - Each split represents a potential (x, y) pairing

    &nbsp;

    **Step 3: Generate valid numbers from a digit string**

    For each part, generate all valid number representations:

    - **Integer form**: Valid only if it doesn't have leading zeros (except for `"0"` itself)
    - **Decimal form**: Try placing a decimal point at each position. Valid only if:
      - The integer part doesn't have leading zeros (unless it's `"0"`)
      - The fractional part doesn't have trailing zeros

    &nbsp;

    **Step 4: Combine results**

    - For each split, compute the Cartesian product of valid x-numbers and valid y-numbers
    - Format each combination as `"(x, y)"`

    &nbsp;

    **Step 5: Return all valid coordinates**

    - Collect all formatted coordinate strings across all splits

  common_pitfalls:
    - title: Missing Edge Cases for Zero
      description: |
        The rules around zeros are subtle:
        - `"0"` is valid as an integer
        - `"00"` is NOT valid (leading zero)
        - `"0.5"` is valid (zero before decimal is allowed)
        - `"0.0"` is NOT valid (trailing zero after decimal)
        - `"10"` is valid, but `"01"` is NOT (leading zero)

        Many solutions fail by not handling these cases correctly. Always test with inputs containing zeros like `"(0123)"` and `"(00011)"`.
      wrong_approach: "Treating all zero-containing strings the same"
      correct_approach: "Separate checks for leading zeros in integers and trailing zeros in decimals"

    - title: Forgetting Single-Digit Numbers
      description: |
        When generating decimal representations, don't forget that a single digit like `"5"` can only be an integer — you can't place a decimal point in a single character.

        Some solutions try to generate `"5."` or `".5"` which are invalid formats.
      wrong_approach: "Trying to insert decimal in single-digit strings"
      correct_approach: "Only try decimal insertion when string length >= 2"

    - title: Incorrect Cartesian Product
      description: |
        The final answer requires combining valid x-coordinates with valid y-coordinates. If either side produces zero valid numbers (due to the zero rules), that entire split should contribute nothing to the answer.

        For example, splitting `"00011"` as `("000", "11")` yields no valid x-coordinates (since `"000"`, `"0.00"`, `"00.0"` are all invalid), so this split produces no results.
      wrong_approach: "Including partial results when one coordinate is invalid"
      correct_approach: "Only combine when both sides have valid representations"

  key_takeaways:
    - "**Decomposition pattern**: Break complex enumeration into independent subproblems (split position × valid numbers)"
    - "**Validation rules matter**: Carefully encode the constraints — leading zeros for integers, trailing zeros for decimals"
    - "**Cartesian product**: When combining independent choices, iterate through all pairs systematically"
    - "**Edge case testing**: Problems with multiple validation rules need thorough testing with boundary inputs like zeros"

  time_complexity: "O(n^4). For each of the O(n) split positions, we generate O(n) valid numbers for each side, and each number generation involves O(n) string operations. The total combinations can be O(n^2) per split."
  space_complexity: "O(n^3). The output list can contain O(n^2) coordinate pairs (O(n) splits × O(n) combinations), each of length O(n)."

solutions:
  - approach_name: Enumeration with Validation
    is_optimal: true
    code: |
      def ambiguous_coordinates(s: str) -> list[str]:
          # Extract digits by removing parentheses
          digits = s[1:-1]
          n = len(digits)
          result = []

          def valid_numbers(string: str) -> list[str]:
              """Generate all valid number representations from a digit string."""
              if not string:
                  return []

              valid = []

              # Try as integer (no decimal point)
              # Valid if no leading zeros, or it's just "0"
              if string == "0" or not string.startswith("0"):
                  valid.append(string)

              # Try with decimal point at each position
              for i in range(1, len(string)):
                  integer_part = string[:i]
                  decimal_part = string[i:]

                  # Integer part: no leading zeros (unless it's "0")
                  if len(integer_part) > 1 and integer_part.startswith("0"):
                      continue

                  # Decimal part: no trailing zeros
                  if decimal_part.endswith("0"):
                      continue

                  valid.append(f"{integer_part}.{decimal_part}")

              return valid

          # Try each split position
          for i in range(1, n):
              left = digits[:i]   # x-coordinate digits
              right = digits[i:]  # y-coordinate digits

              # Get all valid representations for each side
              left_valid = valid_numbers(left)
              right_valid = valid_numbers(right)

              # Cartesian product: combine each valid x with each valid y
              for x in left_valid:
                  for y in right_valid:
                      result.append(f"({x}, {y})")

          return result
    explanation: |
      **Time Complexity:** O(n^4) — For each of O(n) splits, we generate up to O(n) valid numbers per side, and combining them produces O(n^2) pairs. String operations add another O(n) factor.

      **Space Complexity:** O(n^3) — The result list can hold O(n^3) characters total across all coordinate strings.

      The solution systematically tries every split position and every decimal placement, filtering out invalid representations based on the zero rules. The nested loops for Cartesian product ensure we capture all valid combinations.

  - approach_name: Generator-Based Approach
    is_optimal: false
    code: |
      def ambiguous_coordinates(s: str) -> list[str]:
          digits = s[1:-1]

          def generate(string: str):
              """Yield all valid number representations."""
              # Integer representation
              if string == "0" or not string.startswith("0"):
                  yield string

              # Decimal representations
              for i in range(1, len(string)):
                  left, right = string[:i], string[i:]
                  # Check: no leading zeros in integer part (except "0")
                  # Check: no trailing zeros in decimal part
                  if (left == "0" or not left.startswith("0")) and not right.endswith("0"):
                      yield f"{left}.{right}"

          result = []
          n = len(digits)

          # Try all split positions
          for i in range(1, n):
              # Generate Cartesian product of valid coordinates
              for x in generate(digits[:i]):
                  for y in generate(digits[i:]):
                      result.append(f"({x}, {y})")

          return result
    explanation: |
      **Time Complexity:** O(n^4) — Same as the list-based approach.

      **Space Complexity:** O(n^3) — Output dominates; generators reduce intermediate storage.

      This variation uses Python generators (`yield`) instead of building lists, which can be slightly more memory-efficient for the intermediate valid number lists. The logic is identical, but the lazy evaluation avoids storing all valid numbers before combining them.
