Finite Automata: NFA and DFA
3.1 Learning Objectives
By the end of this chapter, you should be able to:
- Define deterministic finite automata (DFA) formally using the 5-tuple
- Trace DFA execution on an input string using the extended transition function
- Define nondeterministic finite automata (NFA) and compute -closure
- Explain why DFAs and NFAs recognize exactly the same class of languages
- Understand at a high level the pipeline from regular expressions to efficient recognizers
3.2 From Specification to Machine
Chapter 2 gave us the language of regular languages: a precise vocabulary for describing which strings belong to a set. Regular expressions are a notation for those descriptions. But a notation alone doesn’t tell us how to run anything.
In this chapter we build the operational counterpart: finite automata, which are simple machines that read strings symbol-by-symbol and decide whether to accept or reject them. We will define two variants — deterministic (DFA) and nondeterministic (NFA) — and prove that both recognize exactly the regular languages. Together with the regular-expression equivalence from Chapter 2, this closes the loop:
This equivalence is not just theoretically satisfying. It drives the practical pipeline used by every lexer generator:
Regular expression → NFA → DFA → Minimized DFA → lexer code Understanding each step — and why we bother with NFAs at all — is the goal of this chapter.
3.3 Deterministic Finite Automata (DFA)
3.3.1 Intuition
A DFA is the simplest possible recognition device. It has:
- A finite set of states representing everything it can “remember”
- A current active state that changes with each input symbol
- A rule — the transition function — mapping (state, symbol) to the next state
- A distinguished start state
- A set of accepting states that determine the outcome
When the DFA finishes reading its input, it either is or is not in an accepting state. That’s the decision.
The critical property: for every state and every symbol, there is exactly one next state. The machine never has a choice. This is what deterministic means.
3.3.2 Formal Definition
Definition (DFA): A deterministic finite automaton is a 5-tuple:
where:
- is a finite set of states
- is the input alphabet
- is the transition function
- is the start state
- is the set of accepting states
The transition function is total: it must be defined for every pair .
3.3.3 Extended Transition Function
To describe how a DFA processes an entire string (not just one symbol), we extend to strings.
Definition: Define by:
In words: to process string , first process from state , then apply on symbol .
Acceptance: DFA accepts string if and only if:
Starting from and processing all of lands in an accepting state.
Language of :
3.3.4 Building a DFA: Worked Example
Task: Design a DFA over that accepts exactly the strings ending with ab.
State design: Think about what information the DFA needs to remember at any point. To know whether the last two characters were ab, the DFA only needs to track what the last character seen was (if anything). Three cases cover everything:
| State | Meaning |
|---|---|
Start; haven’t seen a yet, or last character was b with no preceding a | |
Last character seen was a | |
Last two characters were ab — accepting |
Transition function:
| State | On a | On b |
|---|---|---|
The 5-tuple: where is given by the table.
3.3.5 Tracing Execution
Always trace from the start state, applying the transition function one symbol at a time.
Does accept aab?
Final state . Accept ✓
Does accept aba?
Final state . Reject ✗
Does accept ab?
Final state . Accept ✓
Does accept b?
Final state . Reject ✗
3.3.6 State Diagrams
DFAs are usually drawn as directed graphs:
- Circles represent states
- Labeled directed edges represent transitions
- The start state has an unlabeled incoming arrow
- Accepting states are drawn with double circles
Try it interactively:
Read the visualization using the formal DFA tuple :
- is the set of circles (states)
- is the alphabet (symbols labeling the edges)
- is the unique start state (the one with an incoming arrow from nowhere)
- is the set of accepting states (double circles)
View the DFA as a transition graph where each directed edge means . In the diagram, an arrow from to labeled means exactly .
In a complete DFA, every state has exactly one outgoing edge for each symbol in .
When you click Step, the simulator evaluates one transition and highlights the corresponding edge. The input is accepted if and only if the final highlighted state is in .
3.3.7 Another Example: Divisibility by 3
Task: Design a DFA over that accepts binary strings whose value is divisible by 3.
Key insight: The remainder of a binary number modulo 3 is all the DFA needs to track. There are three possible remainders: 0, 1, 2.
Transition rule: If the current remainder is and we read bit , the new number is . Its remainder mod 3 is .
| State (remainder) | On 0 | On 1 |
|---|---|---|
| (rem 0) | ||
| (rem 1) | ||
| (rem 2) |
Accepting: (remainder 0 means divisible by 3).
Trace: Does 110 (= 6) get accepted?
Final state . Accept ✓ (6 is divisible by 3)
Trace: Does 101 (= 5) get accepted?
Final state . Reject ✗ (5 is not divisible by 3)
Observation: The DFA computes remainder-mod-3 implicitly — each state encodes one remainder. This pattern generalizes: any language whose membership depends on a computable finite summary of the input can be recognized by a DFA with one state per summary value.
3.4 Nondeterministic Finite Automata (NFA)
3.4.1 Motivation
DFAs are efficient to execute but awkward to construct directly from regular expressions. The problem is that DFA design often requires anticipating future input: “is the next symbol the start of pattern , or just part of the preamble?”
NFAs relax this by allowing:
- Multiple possible transitions for the same state and input symbol
- Epsilon () transitions that move between states without consuming input
An NFA can “guess” which branch to follow. It accepts an input if any sequence of choices leads to an accepting state.
This sounds computationally expensive, but it turns out that every NFA can be converted to an equivalent DFA (Section 3.5.2). NFAs are not more powerful — they are just more convenient to design with.
3.4.2 Formal Definition
Definition (NFA): A nondeterministic finite automaton is a 5-tuple:
where:
- , , , are as in a DFA
- is the transition relation
Here is the power set of — the set of all subsets of . So returns a set of states (possibly empty), not a single state.
Key differences from a DFA:
- may return (dead end), a single state, or multiple states
- may be nonempty — -transitions move “for free”
3.4.3 Epsilon-Closure
Before we can define acceptance for NFAs, we need to handle -transitions carefully.
Definition (-closure): For a set of states , the -closure is the set of all states reachable from any state in using zero or more -transitions.
E(S):
result ← S
while result is still changing:
for each q in result:
result ← result ∪ Δ(q, ε)
return result Example: Suppose and .
Then — we follow all -transitions transitively.
3.4.4 Extended Transition for NFAs
We extend to process entire strings, tracking sets of states instead of individual states.
Definition:
In words: to process from state set , first process to get some state set , then apply to every state in and collect all results, then take the -closure of everything collected.
Acceptance: NFA accepts if:
At least one reachable state after processing is accepting.
3.4.5 NFA Example: Contains ab
Task: Build an NFA over that accepts strings containing the substring ab.
Formal Definition:
Transition Relation:
All transitions not listed return .
Trace: Does this NFA accept aab?
- Start:
- Read
a: - Read
a: apply to each state in : - Read
b: apply to each state in :
Since and , the NFA accepts. ✓
3.4.6 Interactive NFA Visualization
Read the visualization using the NFA tuple :
- Active circles show the current reachable state set
- An edge means
- Edges labeled represent transitions that move without consuming input
Each Step computes one expansion:
The simulator highlights the move-on- step followed by the -closure. The input is accepted if and only if the final active-state set intersects .
3.4.7 Why NFAs Are Easier to Build
To understand why compilers use NFAs as an intermediate step, consider the language of strings over that end with ab.
The NFA Approach: Mechanical Construction
NFAs allow nondeterminism, which means the machine can “guess” or follow multiple paths at once. This makes construction mechanical: you simply draw the pattern you want to match.
For the pattern ab, we create three states:
- : The “preamble” state (we haven’t matched the final
abyet). - : We just saw
a(maybe it’s the start of the finalab). - : We just saw
ab(Accepting).
The transitions are straightforward:
The DFA Approach: Explicit State Tracking
In a DFA, we cannot “guess.” We must explicitly track our progress. If we see an a, we move to a state representing “last seen was a.” If we see another a, we stay there. If we see a b, we move to “last seen was ab.” But from that state, if we see another a, we must know to go back to “last seen was a” (because that a could start a new ab).
| Feature | NFA construction | DFA construction |
|---|---|---|
| Logic | Mechanical: Follow the pattern. | Analytical: Track all possible prefixes. |
| Complexity | states for pattern length . | Can require complex “fallback” transitions. |
| Guessing | Enabled: Can be in simultaneously. | Disabled: Must be in exactly one state. |
Takeaway: Humans (and Thompson’s algorithm) find it easy to build NFAs because they only require looking forward at the pattern. DFAs are harder because they require looking backward to handle every possible mismatch.
3.5 The Equivalence Theorem
3.5.1 Statement
The following four models are mathematically equivalent. If a language can be described by one, it can be described by all:
- Regular Languages: Defined by set operations (, concatenation, ).
- Regular Expressions: The notation humans use to specify patterns.
- Nondeterministic Finite Automata (NFA): The mechanical intermediate step.
- Deterministic Finite Automata (DFA): The high-performance implementation.
Why this matters: You can choose whichever representation is most convenient for a given task. Regex are best for specification. DFAs are best for execution. NFAs are best for construction.
3.5.2 Building the Pipeline (High Level)
To make this theorem practical for compilers, we use two fundamental constructions:
- Thompson’s Construction (Regex → NFA): A systematic way to build an NFA by combining small fragments for each regex operator. It is fast and produces machines that are roughly the size of the original expression.
- Subset Construction (NFA → DFA): An algorithm that “simulates” the NFA by tracking all possible states it could be in simultaneously. This produces a DFA that can be executed in time per character.
We will study these algorithms in full detail in Chapter 4.
3.6 DFA vs. NFA: A Precise Comparison
| Property | DFA | NFA |
|---|---|---|
| Transition function | ||
| Epsilon () moves | Not allowed | Allowed (move without input) |
| Execution | Exactly one active state | A set of possible active states |
| Construction | Requires global analysis | Mechanical (Thompson’s algorithm) |
| Performance | per character | $O( |
| State Count | Can be exponentially larger | Linear to the regex length |
3.7 Finite Automata in Lexical Analysis
3.7.1 The lexer Generation Pipeline
The journey from a programmer’s intent to a running scanner follows a strict, automated pipeline:
- Specification
The user defines token patterns using Regular Expressions (e.g.,[0-9]+). - NFA Generation
The generator uses Thompson’s Construction to turn those regexes into an NFA. This is a mechanical process that “glues” state machine fragments together. - DFA Conversion
The Subset Construction algorithm converts the NFA into a DFA. This eliminates nondeterminism and “guesses,” producing a machine that is much faster to run. - Minimization
DFA Minimization merges redundant states, ensuring the final machine uses the minimum possible memory. - Code Emission
The generator outputs source code (Zig, C, etc.) that implements the minimized DFA as a fast transition table orswitchstatement.
Tools like Lex, Flex, and ANTLR automate these steps, allowing you to focus on the token patterns rather than the state machine logic.
3.8 Chapter Summary
This chapter introduced the two machines that bridge the gap between mathematical regular expressions and executable code.
- DFA: Fast to execute, harder to construct directly.
- NFA: Easy to construct from regex, slower to simulate directly.
The Equivalence Theorem guarantees that we can move between these representations without losing expressive power. Chapter 4 will dive into the specific algorithms (Thompson and Subset Construction) that automate these transitions.
3.9 Exercises
DFA Fundamentals
1. For each of the following languages over , design a DFA. Give the formal 5-tuple and draw the state diagram.
a. Strings with an even number of ’s
b. Strings containing the substring aba
c. Strings where the number of ’s is divisible by 3
d. Strings where every is immediately followed by a
2. Given this DFA:
States: {q₀, q₁, q₂} Start: q₀ Accepting: {q₂}
δ(q₀, a) = q₁ δ(q₀, b) = q₀
δ(q₁, a) = q₂ δ(q₁, b) = q₀
δ(q₂, a) = q₂ δ(q₂, b) = q₂ Trace execution on each input and state the outcome:
a. aa
b. aba
c. baa
d. bab
3. Describe (in English) the language recognized by the DFA in Exercise 2. Then write a regular expression for the same language.
NFA Construction
4. Build an NFA for each language. You may use -transitions.
a. Strings over ending with ba
b. Strings over containing 101 as a substring
c. Strings over where the third-to-last character is a
5. For the NFA in part (c) of Exercise 4, trace execution on aabab. Show the active state set after each input symbol.
6. Explain in your own words why NFAs are easier to construct directly from a regular expression than DFAs. Use the example of (a|b)*abb to illustrate.
DFA vs. NFA
7. Fill in the comparison table and justify each entry:
| Property | DFA | NFA |
|---|---|---|
| Transition function type | ||
| -transitions | ||
| Execution speed | ||
| Ease of construction from regex | ||
| Expressive power |
8. Given that NFAs can be exponentially more compact than DFAs, argue both for and against using NFAs directly as lexer implementations (without converting to DFA first).
lexer Pipeline
9. Describe what happens step-by-step when a lexer generator compiles the following token specification. What does each phase of the pipeline produce?
KEYWORD : if | else | while
IDENT : [A-Za-z][A-Za-z0-9]*
INT : [0-9]+
SPACE : [ \t\n]+ Advanced
10. Theoretical: Prove that if is a regular language recognized by an -state DFA, then either is finite or is infinite and contains a string of length between and . (Hint: consider what happens when you trace a path of length exactly through the DFA.)