Nondeterministic Finite Automata

Imagine searching a text editor for the pattern `aab` inside a long string. A <a class="wiki-link" href="/wiki/cs-theory-deterministic-finite-automata" data-track="wiki_article.body.wiki_link">Deterministic Finite Automata</a> needs a carefully engineered state for every possible "memory" of how much of the pattern could still match. A nondeterministic machine sidesteps that engineering problem entirely. It simply guesses which branch of the pattern it is currently trying to match. If any guess leads to acceptance, the string is accepted. This guessing model — trying every option "in parallel" and accepting if some path succeeds — is the essence of a Nondeterministic Finite Automaton (NFA). NFAs matter because they are the natural output of translating a regular expression into a machine. Pattern-matching engines, lexical analyzers in compilers, and network intrusion-detection systems all start by building an NFA directly from a regex's structure. Union, concatenation, and Kleene star each have a simple, local NFA construction. This happens before converting it into something a real processor can execute deterministically, one symbol at a time. Understanding NFAs precisely is therefore the hinge between the algebra of regular expressions and the mechanics of finite-state execution. The big picture is this: nondeterminism is a modeling convenience, not a source of extra computational power. Every language recognized by an NFA is also recognized by some DFA. The two models are equivalent* in expressive power, even though NFAs are often exponentially more concise. That equivalence is proved by the subset construction. It is what lets designers write simple, modular NFAs. They can still deploy them as fast, deterministic table-driven programs. This article gives a rigorous definition of NFAs, including epsilon-transitions. It defines acceptance formally via the extended transition function. It introduces the computation-tree view of nondeterministic execution. It also works a nontrivial example step by step. It sets up — without yet proving — the subset construction that turns any NFA into an equivalent DFA. It also sets up the broader equivalence between finite automata and Regular Expressions.

Key takeaways
  • An NFA may have zero, one, or several transitions on a symbol from a state, plus silent ε-moves.
  • A string is accepted if some computation path ends in an accept state — not all paths need succeed.
  • Nondeterministic acceptance is defined cleanly via ε-closure and an extended transition function δ̂.
  • Every computation on an input can be visualized as a tree of branching states; acceptance means the tree contains an accepting leaf.
  • NFAs and DFAs recognize exactly the same class of languages: the regular languages.
  • NFAs are usually far smaller and easier to construct than equivalent DFAs, especially for regex-like patterns.
The core insight

An NFA accepts a string if at least one of its possibly many parallel computation paths reaches an accepting state, and this "guess-and-check" model recognizes exactly the regular languages — no more, no less.

The idea

A Nondeterministic Finite Automaton is a 5-tuple ``` N = (Q, Σ, δ, q0, F) ``` where `Q` is a finite set of states, `Σ` is a finite alphabet, `q0 ∈ Q` is the start state, `F ⊆ Q` is the set of accept states, and the transition function has signature ``` δ : Q × (Σ ∪ {ε}) → P(Q) ``` Here `P(Q)` denotes the power set of `Q` — the set of all subsets of `Q`. This single change in the codomain, compared to a DFA's `δ : Q × Σ → Q`, is what gives an NFA its extra flexibility in three distinct ways. First, `δ(q, a)` may be the empty set, meaning the machine simply has no move and that branch dies. Second, `δ(q, a)` may contain more than one state, meaning the machine can nondeterministically branch into multiple simultaneous possibilities. Third, `δ` is defined on the special symbol `ε` (the empty string), allowing ε-transitions: silent moves the automaton can take without consuming any input at all. Because a single input symbol can lead to many states, and ε-moves can chain together silently, we must be careful about what a "computation" even means. The clean way to do this is the ε-closure of a set of states — everything reachable using only ε-transitions — and an extended transition function `δ̂` describing where the automaton can be after reading a whole string. A string `w` is accepted if `δ̂(q0, w) ∩ F ≠ ∅`, i.e., if some reachable state after consuming all of `w` is an accepting state. This "exists a path" semantics — as opposed to a DFA's single deterministic path — is the entire conceptual difference between the two models, and it is why NFAs are best pictured not as a single token moving through states but as a whole set of tokens moving in parallel, splitting at branches and vanishing when a branch has no valid move.

How it works

Step 1 — ε-closure. For a state `q`, define `E(q)` (the ε-closure of `q`) as the set of all states reachable from `q` using zero or more ε-transitions. This extends to a set of states `S` by `E(S) = ⋃_{q∈S} E(q)`. Computing `E(q)` is just a graph reachability search (BFS/DFS) over the subgraph of ε-edges.

Step 2 — the extended transition function. Define `δ̂ : Q × Σ → P(Q)` recursively: ``` δ̂(q, ε) = E({q}) δ̂(q, wa) = E( ⋃_{r ∈ δ̂(q,w)} δ(r, a) ) for w ∈ Σ, a ∈ Σ ``` In words: to process the string `wa`, first find every state reachable after reading `w` (that's `δ̂(q,w)`), take every ordinary `a`-transition out of each of those states, union the results, and then close under ε again (since new ε-moves may now be available). Note the base case already closes under ε, so an NFA can accept `ε` itself by starting in an accept state or reaching one via silent moves.

Step 3 — acceptance. The language recognized by `N` is ``` L(N) = { w ∈ Σ* | δ̂(q0, w) ∩ F ≠ ∅ } ``` A string is in the language exactly when at least one of the (possibly many) states the machine could be in after reading it is an accepting state.

The simulation algorithm. In practice you never enumerate all computation paths individually — that would be exponential in the length of the string in the worst case if done naively. Instead you track the set of currently-possible states, exactly mirroring `δ̂`. This is the "breadth-first, keep a frontier set" simulation: ``` function NFA-ACCEPTS(N = (Q, Σ, δ, q0, F), w = a1 a2 ... an): current ← EPSILON-CLOSURE({q0}) for i in 1..n: next ← ∅ for each state q in current: next ← next ∪ δ(q, a_i) current ← EPSILON-CLOSURE(next) if current = ∅: return REJECT // no branch survives; can short-circuit if current ∩ F ≠ ∅: return ACCEPT else: return REJECT function EPSILON-CLOSURE(S): stack ← S copied into a worklist closure ← S while stack not empty: pop q from stack for each r in δ(q, ε): if r ∉ closure: add r to closure push r onto stack return closure ``` This algorithm processes each of the `n` input symbols by advancing an entire subset of `Q` at once — never more than `|Q|` states are tracked simultaneously, regardless of how many nondeterministic branches conceptually exist. This is precisely the idea that will later be formalized as the subset construction, turning an NFA into an equivalent DFA whose states are subsets of `Q`. The following state diagram shows a small NFA over `Σ = {a, b}` recognizing strings that end in `ab`, built with a deliberately nondeterministic guess about "where the suffix ab begins":

Figure
NFA recognizing strings ending in "ab", with a nondeterministic branch at q0
q0 start q1 q2 (accept) a, b a b q0 nondeterministically both "stays put" and "guesses this a starts the suffix ab"

Here `δ(q0, a) = {q0, q1}` — on reading an `a`, the machine forks: one copy stays in `q0` (in case this `a` is not the start of the final `ab`), another moves to `q1` (guessing that it is). Only the branch that correctly guesses the last occurrence of `a` before a final `b` survives to `q2`. No single DFA state can encode "maybe this is the start of the suffix" without extra bookkeeping — which is exactly why the DFA for this same language needs more states (tracking how much of the suffix `ab` has been matched most recently) than this compact 3-state NFA.

Complexity & correctness

Time complexity of simulation. Processing a string of length `n` with the frontier-set algorithm above costs, per symbol, an ε-closure computation and a union over at most `|Q|` states' transition sets. Each ε-closure is a graph traversal over `|Q|` states and at most `|Q|²` ε-edges (worst case a complete ε-graph), costing `O(|Q| + |Q|²) = O(|Q|²)`. So each of the `n` steps costs `O(|Q|²)` in the worst case (or `O(|Q| · |δ|)` counting actual transitions), giving ``` Total time = O(n · |Q|²) Space = O(|Q|) (just the current frontier set) ``` This is polynomial and in fact comparable to DFA simulation up to the constant factor of `|Q|` versus 1 — you are simulating up to `|Q|` deterministic "threads" at once, not an exponential number of individual paths. This matters: naively trying every nondeterministic path separately (a literal computation tree with branching factor up to `|Q|` at each step) would cost `O(|Q|^n)` in the worst case, but tracking the set of reachable states collapses all of that redundancy, since many paths merge into the same state.

Correctness of the simulation. The invariant maintained by the loop is: > After processing `a1 ... ai`, the variable `current` equals `δ̂(q0, a1...ai)` exactly. Base case: before any symbols are read, `current = E({q0}) = δ̂(q0, ε)` by definition of `δ̂` on the empty string. Inductive step: assume `current = δ̂(q0, a1...ai)` holds before processing `a_{i+1}`. The algorithm computes `next = ⋃_{q ∈ current} δ(q, a_{i+1})`, then sets `current ← E(next)`. By the recursive definition of `δ̂`, ``` δ̂(q0, a1...ai a_{i+1}) = E( ⋃_{r ∈ δ̂(q0, a1...ai)} δ(r, a_{i+1}) ) = E( ⋃_{r ∈ current} δ(r, a_{i+1}) ) ``` which is exactly what the algorithm computes. By induction the invariant holds after all `n` symbols, so `current = δ̂(q0, w)`, and checking `current ∩ F ≠ ∅` correctly decides membership by the definition of `L(N)`. ∎

Equivalence with DFAs (stated, not proved here). Every NFA can be converted to an equivalent DFA whose states are subsets of the NFA's states — precisely the sets `current` computed above, reified as first-class states rather than transient variables. This is the subset construction, and it shows NFAs and DFAs are equally expressive: both classes capture exactly the regular languages. The catch is size: the subset-construction DFA can have up to `2^{|Q|}` states, and this blow-up is sometimes unavoidable (there exist NFA families whose minimal equivalent DFA really does grow exponentially). So the tradeoff is real: NFAs buy you succinct, modular descriptions (especially when built compositionally from regular expressions) at the cost of needing a conversion step — or a slower simulation — to run them deterministically at high speed.

Honesty about the model. Nondeterminism here is not parallelism in the sense of extra computational power (unlike, say, nondeterministic Turing machines and the P vs NP question); it is a closed, provably eliminable convenience for finite automata. This is a crucial contrast to keep in mind, and it is one of the cleanest "nondeterminism costs you nothing in the limit, but can cost you exponentially in representation size" stories in the theory of computation.

Worked example

Consider an NFA recognizing all strings over `{0,1}` containing `01` or ending in `1` — chosen deliberately non-symmetric so guessing genuinely matters and no trivial shortcut trace hides a bug. ``` Q = {s, p, q, r} Σ = {0, 1} q0 = s F = {q, r} δ(s, 0) = {s, p} δ(s, 1) = {s, r} δ(s, ε) = {} δ(p, 1) = {q} δ(p, 0) = {} δ(p, ε) = {} δ(q, 0) = {q} δ(q, 1) = {q} δ(q, ε) = {} δ(r, 0) = {} δ(r, 1) = {r} δ(r, ε) = {} ``` Reading: from `s` the machine always keeps a "stay in `s`" branch alive (to keep scanning for a future `01` or trailing `1`), while also guessing when a `0` might be the start of a `01` (branch to `p`), or when a `1` might be the final symbol (branch to `r`, which only survives on more `1`s). State `q` is a sink accept state reached once an actual `01` has been confirmed; state `r` is an accept state that survives only on trailing `1`s. Trace input `w = 1001`:

StepSymbol read`current` beforeTransitions taken`current` after (closed under ε — none here)
0———`{s}`
1`1``{s}``δ(s,1) = {s, r}``{s, r}`
2`0``{s, r}``δ(s,0)={s,p}`, `δ(r,0)=∅``{s, p}`
3`0``{s, p}``δ(s,0)={s,p}`, `δ(p,0)=∅``{s, p}`
4`1``{s, p}``δ(s,1)={s,r}`, `δ(p,1)={q}``{s, r, q}`

After all 4 symbols, `current = {s, r, q}`. Since `q ∈ F` (indeed also `r ∈ F`), `current ∩ F = {q, r} ≠ ∅`, so ACCEPT. This is correct: `1001` contains the substring `01` (positions 2–3) — the `p → q` branch is confirming exactly that — and it also ends in `1`, confirmed independently by the `r` branch surviving. Both "reasons" to accept are witnessed simultaneously as two live states in the frontier, illustrating that acceptance needs only one surviving accepting branch, even when several exist. Contrast with `w = 100`: after symbol `1`, `current = {s, r}`; after the first `0`, `current = {s, p}` (the `r` branch dies since `δ(r,0)=∅`); after the second `0`, `current = {s, p}` again (`p` cannot survive a `0`). Final `current = {s, p}`, and `{s,p} ∩ F = ∅`, so REJECT — correctly, since `100` neither contains `01` nor ends in `1`.

Common pitfalls & traps

PitfallWhy it happensFix
Forgetting to ε-close the initial frontier `{q0}`Treating the start state as if it alone is `current`, ignoring silent reachable statesAlways initialize `current ← E({q0})`, not `{q0}`
Forgetting to re-close after every symbol, not just onceε-transitions can become newly available after consuming a symbolApply `E(·)` inside the loop, every iteration
Confusing "no transition defined" with "reject immediately"In a DFA a missing transition is a hard error (or an implicit dead state); in an NFA it just means one branch vanishesOnly reject if every branch has died (`current = ∅`) or none of the survivors are accepting at the end
Assuming nondeterminism means "average-case parallel speedup"Sounds like real parallel hardware, but it is a modeling abstractionSimulate via the subset-tracking algorithm — cost is `O(nQ²)`, not magically `O(n)` extra-fast
Believing accepting requires all paths to acceptMuddling NFA semantics with universal (∀) quantification, as in alternating automataRemember: NFA acceptance is existential — one accepting path suffices
Building an ε-loop and not detecting it in closure computationInfinite recursion if `EPSILON-CLOSURE` doesn't track visited statesAlways mark states visited before/while pushing to the worklist

Pioneers & history

Origins · 1 minMichael O. Rabin and how this was discovered
MO
Michael O. Rabin
1931– · Israeli-American computer scientist, co-inventor of nondeterministic automata theory

Rabin, working with Dana Scott at IBM in the late 1950s, formalized nondeterministic finite automata as a mathematical tool and proved — in their landmark 1959 paper — that nondeterminism adds no expressive power over deterministic finite automata, a result now cited as one of the founding theorems of automata theory. This Rabin–Scott theorem underlies the subset construction taught in every course on finite automata, and the two shared the 1976 Turing Award for this and related contributions to computational complexity and computability.

History
1943
— McCulloch and Pitts model neural activity with finite-state-like logical networks, a conceptual ancestor of automata theory.
1956
— Kleene proves regular expressions describe exactly the languages recognized by finite automata.
1959
— Rabin and Scott introduce nondeterministic finite automata and prove their equivalence to DFAs via the subset construction.
1968
— Thompson publishes a direct construction turning regular expressions into NFAs, foundational to text-search tools like `grep`.
1976
— Rabin and Scott receive the Turing Award for their work on automata and computational complexity.

Where this matters

Compiler front-ends build NFAs directly from the regular-expression grammar of a lexical specification (Thompson's construction), then convert to DFAs via subset construction for fast, table-driven scanning — this pipeline underlies tools like `lex`/`flex` and the tokenizers inside virtually every production compiler and interpreter. Text-search utilities and regex engines (`grep`, `ripgrep`, the regex libraries embedded in scripting languages) either compile patterns to NFAs and simulate them with the frontier-set algorithm described here, or convert to DFAs for raw speed at the cost of memory. Network intrusion detection systems and deep-packet-inspection hardware use automaton-based pattern matchers — often NFA-like structures compiled into specialized hardware or bytecode — to scan traffic for signatures without backtracking. XML and JSON schema validators, protocol parsers, and even some game-AI dialogue systems use NFA-style branching state machines whenever "several interpretations of the input are simultaneously plausible" is a natural way to specify behavior. Understanding NFAs is also the essential stepping stone to the NFA-to-DFA Subset Construction, and more broadly to the Equivalence of Regular Expressions and Finite Automata, the theorem that ties together three independently motivated formalisms — regex syntax, deterministic machines, and nondeterministic machines — into one unified notion of "regular language."

Ask the tutor: How exactly does the subset construction turn the frontier-set simulation of an NFA into a full DFA, and why can that DFA sometimes require exponentially many states?

Is an NFA more powerful than a DFA?

No — not in terms of which languages can be recognized. The Rabin–Scott theorem, realized through the subset construction, shows that for every NFA there is an equivalent DFA recognizing exactly the same language, and vice versa (every DFA is trivially also an NFA). What NFAs offer is succinctness and ease of construction: they let you describe a language compositionally, mirroring the structure of a regular expression, without manually tracking all the bookkeeping a DFA needs. The cost of that convenience is paid later, during conversion, where the equivalent DFA can have exponentially more states in the worst case.

What exactly is an ε-transition, and why allow it?

An ε-transition lets the automaton change state without consuming any input symbol — it is a "free" or "silent" move. It is useful because it makes certain constructions modular: for example, to build an NFA for the concatenation of two languages `L1L2`, you can simply add an ε-edge from every accept state of the `L1`-NFA to the start state of the `L2`-NFA, without redesigning either machine's internal transitions. This modularity is exactly what makes Thompson's construction (regex → NFA) compositional and simple to implement.

How is δ(q, a) = ∅ different from a DFA's transition function being undefined?

In a DFA, `δ` is typically required to be total — every state has exactly one transition per symbol, sometimes achieved by adding an explicit dead/trap state. In an NFA, `δ(q, a) = ∅` is a completely normal, expected outcome: it means the branch currently in state `q` simply dies when it reads `a`, and it is dropped from the frontier set. The automaton only rejects if every branch has died, or if none of the surviving branches at the end of input are accepting states.

Can an NFA have multiple start states?

The standard definition (as given here) uses a single start state `q0`, but this is not a loss of generality: a common variant allows a set of start states, and it is trivially equivalent to the single-start-state model, since you can add one new start state connected by ε-transitions to each of the "true" start states. This is often used, again, for compositional constructions like the union of two regular expressions' NFAs.

Does simulating an NFA require exponential time, since it can branch exponentially?

No, and this is a common misconception. Although the computation tree of an NFA can have exponentially many leaves (one per possible sequence of branch choices), you never need to enumerate them individually. Tracking the set of currently reachable states — never more than `|Q|` states, since `Q` is finite — collapses all of that redundancy. This gives simulation time `O(n · |Q|²)`, polynomial in both the string length and the automaton size, not exponential.

How does an NFA relate to a regular expression?

Every regular expression can be translated into an NFA via Thompson's construction: base cases (single symbols, ε, ∅) become tiny 1- or 2-state NFAs, and the regex operators — union, concatenation, and Kleene star — each correspond to a simple, local gluing-together of smaller NFAs using ε-transitions. Conversely, every NFA can be converted back into an equivalent regular expression (for instance via the state-elimination method). This three-way equivalence — regex, NFA, DFA — is what people mean when they say all three formalisms define exactly the "regular languages."

Why do textbooks introduce NFAs at all if DFAs can recognize the same languages?

Because NFAs are dramatically easier to design and combine. Building a DFA directly for a complex pattern often requires careful case analysis to track "how much of the pattern could I still be matching," while an NFA lets you encode each piece of the pattern with its own small set of states and let ε-transitions and branching handle the combination automatically. This is precisely the practical workflow used by regex engines and lexer generators: write the NFA structurally, then let an algorithm handle the DFA conversion (or simulate the NFA directly) mechanically.

Before this

Where this leads