title: Camelcase Matching
slug: camelcase-matching
difficulty: medium
leetcode_id: 1023
leetcode_url: https://leetcode.com/problems/camelcase-matching/
categories:
  - strings
  - arrays
  - two-pointers
patterns:
  - slug: two-pointers
    is_optimal: true

function_signature: "def camelMatch(queries: list[str], pattern: str) -> list[bool]:"

test_cases:
  visible:
    - input: { queries: ["FooBar", "FooBarTest", "FootBall", "FrameBuffer", "ForceFeedBack"], pattern: "FB" }
      expected: [true, false, true, true, false]
    - input: { queries: ["FooBar", "FooBarTest", "FootBall", "FrameBuffer", "ForceFeedBack"], pattern: "FoBa" }
      expected: [true, false, true, false, false]
    - input: { queries: ["FooBar", "FooBarTest", "FootBall", "FrameBuffer", "ForceFeedBack"], pattern: "FoBaT" }
      expected: [false, true, false, false, false]
  hidden:
    - input: { queries: ["a"], pattern: "a" }
      expected: [true]
    - input: { queries: ["AB", "aB", "Ab"], pattern: "A" }
      expected: [false, false, true]
    - input: { queries: ["abc"], pattern: "ABC" }
      expected: [false]
    - input: { queries: ["aksvbjLiknuTzqon", "ksvjLimflkpnTzqn", "mmkasvjLiknTxzqn", "ksvjLiurknTzzqbn", "ksvsjLctikgnTzqn", "knaborsvjLiworknTzqn"], pattern: "ksvjLiknTzqn" }
      expected: [true, true, true, false, false, false]

description: |
  Given an array of strings `queries` and a string `pattern`, return a boolean array `answer` where `answer[i]` is `true` if `queries[i]` matches `pattern`, and `false` otherwise.

  A query word `queries[i]` matches `pattern` if you can insert lowercase English letters into the pattern so that it equals the query. You may insert a character at any position in pattern or you may choose not to insert any characters **at all**.

constraints: |
  - `1 <= pattern.length, queries.length <= 100`
  - `1 <= queries[i].length <= 100`
  - `queries[i]` and `pattern` consist of English letters

examples:
  - input: 'queries = ["FooBar","FooBarTest","FootBall","FrameBuffer","ForceFeedBack"], pattern = "FB"'
    output: "[true,false,true,true,false]"
    explanation: '"FooBar" can be generated like this "F" + "oo" + "B" + "ar". "FootBall" can be generated like this "F" + "oot" + "B" + "all". "FrameBuffer" can be generated like this "F" + "rame" + "B" + "uffer".'
  - input: 'queries = ["FooBar","FooBarTest","FootBall","FrameBuffer","ForceFeedBack"], pattern = "FoBa"'
    output: "[true,false,true,false,false]"
    explanation: '"FooBar" can be generated like this "Fo" + "o" + "Ba" + "r". "FootBall" can be generated like this "Fo" + "ot" + "Ba" + "ll".'
  - input: 'queries = ["FooBar","FooBarTest","FootBall","FrameBuffer","ForceFeedBack"], pattern = "FoBaT"'
    output: "[false,true,false,false,false]"
    explanation: '"FooBarTest" can be generated like this "Fo" + "o" + "Ba" + "r" + "T" + "est".'

explanation:
  intuition: |
    Think of this problem as a **subsequence matching** task with a twist: uppercase letters are "anchors" that must match exactly, while lowercase letters are flexible.

    Imagine the pattern as a skeleton and the query as a potential full word. You're checking whether the query could have been built by "expanding" the pattern — inserting lowercase letters wherever you want, but never adding extra uppercase letters.

    The key insight is that **uppercase letters are mandatory checkpoints**. If you encounter an uppercase letter in the query that doesn't match the next expected character in the pattern, the match fails immediately. Lowercase letters in the query, however, can be "absorbed" freely — they might be insertions that don't need to match anything in the pattern.

    Think of it like this: walk through both strings simultaneously. Pattern characters must appear in the query in order, and any uppercase letter in the query that isn't from the pattern breaks the match.

  approach: |
    We solve this using a **Two Pointer Approach** to check if each query matches the pattern:

    **Step 1: Create a helper function to match one query**

    - Use a pointer `j` to track position in the pattern
    - Iterate through each character in the query

    &nbsp;

    **Step 2: For each character in the query**

    - If `j < len(pattern)` and the current query character equals `pattern[j]`, advance `j` (we matched a pattern character)
    - Otherwise, if the query character is **uppercase**, return `False` — this is an unmatched uppercase letter, which means the query has an extra "anchor" not in the pattern

    &nbsp;

    **Step 3: Check if pattern was fully matched**

    - After processing all query characters, return `True` only if `j == len(pattern)`
    - If `j` hasn't reached the end, the pattern wasn't fully consumed, so the match fails

    &nbsp;

    **Step 4: Apply to all queries**

    - Return a list of boolean results, one for each query

    &nbsp;

    This approach works because we greedily match pattern characters in order while ensuring no extra uppercase letters appear in the query.

  common_pitfalls:
    - title: Ignoring Uppercase Letter Constraints
      description: |
        A common mistake is treating this as a simple subsequence problem. If you only check that the pattern is a subsequence of the query, you'll miss cases where the query has extra uppercase letters.

        For example, with `pattern = "FB"` and `query = "ForceFeedBack"`, the pattern `FB` is technically a subsequence, but the query contains extra uppercase letters (`F`, `B` at "ForceFeedBack") that aren't in the pattern.

        The rule is: **any uppercase letter in the query that doesn't match the current pattern character is a deal-breaker**.
      wrong_approach: "Simple subsequence check"
      correct_approach: "Track both subsequence matching AND reject unmatched uppercase letters"

    - title: Not Consuming the Entire Pattern
      description: |
        Another pitfall is forgetting to verify that the entire pattern was matched. If you reach the end of the query but haven't consumed all of the pattern, the match should fail.

        For example, with `pattern = "FoBaT"` and `query = "FooBar"`, you can match `F`, `o`, `B`, `a`, but there's no `T` in the query. The pattern index won't reach the end, so this should return `False`.
      wrong_approach: "Only checking query traversal completes"
      correct_approach: "Verify pattern pointer reaches the end after query traversal"

    - title: Confusing Character Case Rules
      description: |
        The rules are asymmetric:
        - **Pattern characters** (both upper and lower) must appear in the query in order
        - **Extra lowercase** in the query: OK (these are the "insertions")
        - **Extra uppercase** in the query: NOT OK (no uppercase can be inserted)

        If you confuse these rules, you might incorrectly allow queries with extra uppercase letters or reject valid matches with extra lowercase letters.

  key_takeaways:
    - "**Subsequence with constraints**: This is a twist on the classic subsequence problem — uppercase letters act as mandatory anchors"
    - "**Two-pointer pattern**: Maintain a pointer in the pattern and iterate through the query, advancing the pattern pointer only on matches"
    - "**Greedy matching**: Match pattern characters as soon as possible; this greedy approach works because we need all pattern characters in order"
    - "**Character classification matters**: Distinguishing uppercase vs lowercase is the key insight that makes this problem interesting"

  time_complexity: "O(n × m) where `n` is the number of queries and `m` is the maximum length of a query. We process each character of each query exactly once."
  space_complexity: "O(n) for the output array. The matching itself uses O(1) extra space per query."

solutions:
  - approach_name: Two Pointers
    is_optimal: true
    code: |
      def camelMatch(queries: list[str], pattern: str) -> list[bool]:
          def matches(query: str) -> bool:
              # Pointer for pattern
              j = 0
              for char in query:
                  # If pattern character matches, advance pattern pointer
                  if j < len(pattern) and char == pattern[j]:
                      j += 1
                  # Unmatched uppercase letter = automatic failure
                  elif char.isupper():
                      return False
              # Pattern must be fully consumed
              return j == len(pattern)

          return [matches(query) for query in queries]
    explanation: |
      **Time Complexity:** O(n × m) — For each of `n` queries, we iterate through up to `m` characters.

      **Space Complexity:** O(n) — Output array of `n` booleans.

      The two-pointer technique efficiently checks each query in a single pass. We greedily match pattern characters while rejecting any query that contains unmatched uppercase letters.

  - approach_name: Regular Expression
    is_optimal: false
    code: |
      import re

      def camelMatch(queries: list[str], pattern: str) -> list[bool]:
          # Build regex: each pattern char followed by optional lowercase
          # e.g., "FB" -> "^[a-z]*F[a-z]*B[a-z]*$"
          regex_parts = ["^[a-z]*"]
          for char in pattern:
              regex_parts.append(char)
              regex_parts.append("[a-z]*")
          regex_parts.append("$")
          regex = re.compile("".join(regex_parts))

          return [bool(regex.match(query)) for query in queries]
    explanation: |
      **Time Complexity:** O(n × m) — Regex matching is linear in query length.

      **Space Complexity:** O(p) for the compiled regex where `p` is pattern length.

      This approach builds a regex that matches any string where:
      1. Pattern characters appear in order
      2. Only lowercase letters can appear between pattern characters

      For `pattern = "FB"`, the regex becomes `^[a-z]*F[a-z]*B[a-z]*$`. This elegantly captures the matching rules but is less intuitive than the two-pointer approach.
