skip to content

questions

20

In a regular expression, what is the difference between a greedy quantifier and a lazy one?

level: juniorimportance: must knowfreq 70%

answer

  1. same language, different preferred match
  2. how much does the repeat take first?
  3. greedy takes all, then gives back
  4. lazy starts empty, grows by one
  5. a negated class avoids the question

basics

~20 s

A greedy quantifier takes as many repetitions as it can and gives characters back only when the rest of the pattern fails; a lazy one takes as few as possible and grows one at a time.

solid answer

~40 s

Both forms accept exactly the same strings; they differ in which match is preferred, and therefore in what you read out afterwards. `.*` first consumes as far as it can, then releases one character at a time until the remainder of the pattern can succeed, so it settles on the longest run that still works. `.*?` starts at zero repetitions and extends one at a time, so it settles on the shortest run that works. On the line `user=alice;action=login;` the pattern `=(.*);` captures `alice;action=login`, because the greedy run gives back only as far as the *last* `;`. Written lazily, `=(.*?);` captures `alice`. The preference is local to that quantifier — it is not a promise about the length of the overall match.

go deeper

for a junior

Remember the direction of each: greedy grabs the most it can and backs off, lazy grabs the least and grows. Be able to say which one stops at the first delimiter in a key=value line.

for a middle

Explain that both forms accept the same language and only the preference order differs, and show the backtracking sequence that leaves the longest run in the capture group.

for a senior

Show the habit of reading every unrestricted run in a pattern and asking what it can swallow on a malformed line, then replacing it with a negated class so the boundary is stated rather than inferred.

for a principal

Frame it as a contract question: patterns that rely on a preference order to stop in the right place are fragile when input shape drifts, so a house style that requires explicit delimiters pays for itself across a fleet of extractors.

## The two forms describe the same language A **quantifier** says how many times the element before it may repeat: `*` is zero or more, `+` is one or more, `{2,5}` is a bounded repeat. Writing `?` after a quantifier makes it **lazy** (also called reluctant or non-greedy): `*?`, `+?`, `{2,5}?`. The first thing to pin down is what does *not* change. A greedy pattern and its lazy twin accept exactly the same set of strings — as formal languages they are identical, and a machine built from either recognises the same inputs. What changes is which of the many valid matches is **preferred**, and therefore what ends up in the capture groups. That matters because a pattern applied to real input almost always has several valid matches and you only ever see one of them. ## Greedy: take everything, then give it back A greedy quantifier is not a promise to swallow the rest of the input; it is an **order of attempts**. Applying `=(.*);` to `user=alice;action=login;`: 1. The match begins at the leftmost position where any match can start — here the first `=`, at offset 4. 2. `.*` takes everything to the end of the line. 3. The pattern still needs a `;`, and there is nothing left, so `.*` gives one character back. 4. It keeps giving characters back until a `;` sits at the current position — which happens at the **final** `;`. 5. The whole match is `=alice;action=login;` and group 1 holds `alice;action=login`. The field extractor was meant to pull one field and it swallowed two. Nothing malfunctioned: the pattern asked for the longest run of any character that still leaves a `;` behind, and that is what it got. ## Lazy: take nothing, then grow A lazy quantifier reverses the order. `=(.*?);` starts `.*?` at zero characters, checks whether a `;` follows, and only if that fails does it take one more character. On the same line it stops the moment the first `;` appears, so group 1 holds `alice`. Again nothing about the *possible* matches changed — only which one is tried first. ## The same line, three patterns | pattern | whole match on `user=alice;action=login;` | group 1 | |---|---|---| | `=(.*);` | `=alice;action=login;` | `alice;action=login` | | `=(.*?);` | `=alice;` | `alice` | | `=([^;]*);` | `=alice;` | `alice` | The third row is the one experienced authors reach for. A **negated character class** — "any character that is not a delimiter" — states the intent directly instead of relying on a preference order to stop in the right place, and it reads the same to every engine. ## Choosing between them - Use **lazy** when the closing delimiter is the *first* one after the field: quoted values, tag-like wrappers, a key up to the first separator. - Use **greedy** when you genuinely want the widest span: everything up to the last separator, a trailing remainder, the final extension in a name. - Prefer a **negated class** whenever the boundary is a single known character; it removes the question entirely. - Do not assume lazy is cheaper. If the text you are looking for sits far to the right, a lazy quantifier extends one character at a time all the way there, while a greedy one may land near it immediately. Which is cheaper depends on the input, not on the marker. ## What neither form changes - **The set of strings the pattern accepts.** Only the preferred match changes. - **Where the match starts.** The starting position is chosen first — the leftmost position at which any match exists — and the quantifier's preference only decides among the matches available there. - **The meaning of the surrounding pattern.** Anchors, classes and group numbering behave identically either way. - **Whether the overall match is the longest one possible.** Greedy expresses a preference at one quantifier; an earlier part of the pattern can still settle on a shorter overall span. The practical habit is to read every `.*` in a pattern and ask "what is it allowed to swallow on a bad line?". A field extractor that looks correct on one well-formed sample and returns too much on the next one almost always has a greedy run crossing a delimiter it was never meant to cross.

  • Does making a quantifier lazy change which strings the pattern can match at all?
    No. The two forms accept the same language; an input either has a match or it does not, regardless of the marker. What changes is which of the valid matches is reported, and therefore the spans and captures you read out afterwards.
  • How would you rewrite `=(.*?);` without using a lazy quantifier?
    Use a negated character class: `=([^;]*);`. It says directly that the field contains no delimiter, so there is only one match to find rather than a preference order that happens to stop in the right place, and it reads identically under every match-semantics convention.

Greedy is a shopper who fills the bag to the top and then puts items back until it closes; lazy is one who puts in a single item and adds another only when told it is not enough.

saying these in an interview costs you the question

  • Says greedy and lazy versions accept different sets of strings
  • Thinks a lazy quantifier is always faster than a greedy one
  • Believes a lazy quantifier can never span a long piece of text
  • Treats greedy as a guarantee of the longest overall match
  • Puts an unrestricted run between two delimiters and blames the engine
open as a page

In a route-matching pattern, what does the Kleene star applied to a subexpression mean?

level: juniorimportance: must knowfreq 72%

basics

~20 s

The Kleene star means zero or more repetitions of the subexpression it follows, joined end to end. Zero is the trap: a starred part can match the empty string, so on its own it never forces that text to be present.

open as a page

Why can a regex engine that advances a set of states in one pass not support backreferences?

level: middleimportance: must knowfreq 52%

basics

~20 s

A live state set records where the pattern is, not what the text matched. A backreference demands that an arbitrarily long captured substring occur again, and no fixed set of machine states can carry that text forward.

open as a page

A regex engine can run a pattern by backtracking or by advancing a set of states - how does each execute a match?

level: middleimportance: must knowfreq 64%

basics

~10 s

A backtracking engine explores one alternative at a time and rewinds the input when a branch fails. A state-set engine advances every live alternative together, consuming each input character exactly once.

open as a page

Why does an unanchored regular expression accept input that its author meant to reject?

level: middleimportance: must knowfreq 58%

basics

~20 s

Searching asks whether a match exists anywhere in the subject, not whether the whole subject matches. Without a start and end anchor, a pattern for four digits happily matches the four digits buried inside a longer string.

open as a page

Which three operators does every regular pattern reduce to, and how do they bind relative to one another?

level: middleimportance: must knowfreq 58%

basics

~10 s

Concatenation, alternation and the Kleene star. Repetition binds tightest, then concatenation, then alternation, so ab*|c reads as (a(b*))|c. Parentheses exist only to override that order.

open as a page

Which shape in a regular-expression validation pattern makes its matching work grow exponentially with the length of one input field?

level: middleimportance: must knowfreq 72%

basics

~20 s

Ambiguity: a quantifier nested inside another quantifier, or alternation branches that can match the same text. A backtracking matcher then has exponentially many ways to split one input, and on a failing input it tries them all.

open as a page

In a regular expression with nested capture groups, how are the group numbers assigned?

level: middleimportance: should knowfreq 48%

basics

~20 s

Groups are numbered by the left-to-right order of their opening parentheses, so an outer group always has a lower number than the groups nested inside it. Group 0 is the whole match, and non-capturing groups take no number.

open as a page

In a matching rule, what do character classes, the optional mark and bounded repeats desugar to?

level: middleimportance: should knowfreq 45%

basics

~20 s

All three are shorthand over the three operators. A class is an alternation of its members, R? is R or the empty text, R+ is RR*, and R{2,4} is RR(R(R)?)?. None of them lets a pattern describe anything new.

open as a page

Why does a backtracking-prone validation pattern hang on a long near-match input rather than on an obviously wrong one?

level: middleimportance: should knowfreq 46%

basics

~20 s

A backtracking matcher stops at the first path that succeeds, so only failure forces it to eliminate every alternative. An input that satisfies the ambiguous part for a long prefix and then defeats the rest maximises the number of paths to eliminate.

open as a page

What does a regex engine's lazy DFA cache store, and what happens when it reaches its memory cap?

level: seniorimportance: should knowfreq 42%

basics

~20 s

A lazy DFA cache stores subset-construction results computed on demand: each distinct set of live states becomes one deterministic state with its transitions. At the cap the engine flushes and rebuilds, or falls back to simulating the state set directly.

open as a page

Why can one regular-expression engine return a shorter match than another for the same alternation?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Two different match-selection rules are in play. Leftmost-first returns the first match the pattern's own preference order reaches, so branch order decides; POSIX leftmost-longest returns the longest match available at that position, whatever the order.

open as a page

What does Kleene's theorem say about the relationship between the three regular operators and finite automata?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Kleene's theorem says patterns and finite automata describe exactly the same languages, in both directions: every pattern built from concatenation, alternation and the star has an equivalent finite automaton, and every finite automaton has an equivalent pattern.

open as a page

A shipped validation pattern backtracks catastrophically on crafted input: how do you rewrite it so that matching cannot blow up?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Remove the ambiguity so every accepted string has exactly one way to match: flatten a quantifier nested in a quantifier, make alternation branches disjoint, and express a separated list as item followed by repeated separator-item. Then anchor, cap the input and prove it with a growth test.

open as a page

Your ingest filter runs hundreds of regex patterns over every record under a tight latency budget - which engine architecture do you choose?

level: principalimportance: should knowfreq 38%

basics

~20 s

Choose by who authors the patterns and what the budget must guarantee. Untrusted or frequently changed patterns on a hot path argue for a one-pass engine with a worst-case bound; a small reviewed set needing richer features can justify backtracking.

open as a page

Your platform lets non-engineers supply regular-expression rules that run inside request handling: how do you keep one pathological pattern from taking the service down?

level: principalimportance: should knowfreq 31%

basics

~20 s

Treat a supplied pattern as untrusted code. Restrict what authors may express, screen patterns at admission for ambiguous shapes, cap input length, run each match under a budget, and isolate execution so a stuck match consumes one bounded, abandonable slot rather than a request worker.

open as a page

Which shape in a regular-expression search makes its cost grow quadratically with input length rather than exponentially?

level: middleimportance: nice to knowfreq 27%

basics

~20 s

An unanchored search whose pattern starts with a quantifier: the matcher retries the whole attempt at every starting offset, and each attempt can scan most of the input. That gives about n(n+1)/2 steps, quadratic rather than exponential.

open as a page

How can one automaton match hundreds of patterns against a record in a single pass over the input?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Combine the patterns into one machine by alternation and tag every accepting state with the pattern it came from. One scan then advances the combined state set per character and reports each pattern whose accepting state is reached.

open as a page

Why can a pattern that is able to match the empty string stall a global find-and-replace?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

A global scan continues from the end of the previous match. A zero-width match ends where it began, so the next attempt starts at the same position and succeeds again, forever — unless the engine forces the position forward by one.

open as a page

Why is a pattern containing a backreference no longer a regular expression in the formal sense?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

A backreference demands that a later span reproduce text captured earlier, which the three operators cannot express. Patterns using one can describe non-regular languages, so no equivalent finite automaton exists and Kleene's theorem stops applying.

open as a page