Introduction to Theory of Computation

Alphabets, strings and languages, operations on languages, the four kinds of machines and grammars of the Chomsky hierarchy, and what "decidable" means.

Theory of Computation asks what problems machines can solve at all, and with how much memory. It studies simple mathematical models of computers (automata), the languages they recognise, and the grammars that generate those languages.

Alphabets, strings and languages

  • An alphabet Σ is a finite, non-empty set of symbols, such as Σ = {0, 1} or Σ = {a, b}.
  • A string over Σ is a finite sequence of symbols: 0110, abba. The empty string is written ε, and |w| is the length of w (|ε| = 0).
  • Σk is the set of all strings of length k; there are |Σ|k of them. Σ* is the set of all strings (including ε), and Σ⁺ = Σ* − {ε}.
  • A language is any set of strings over Σ, that is, any subset of Σ*. It can be finite ({ε, 01, 10}) or infinite ({0ⁿ1ⁿ | n ≥ 0}).

Two special languages are easy to confuse: ∅ (no strings at all) and {ε} (exactly one string, the empty one).

For a string w: a prefix is a start of w, a suffix an end, and a substring a contiguous piece. A string of length n has n + 1 prefixes and n + 1 suffixes (counting ε and w itself), and at most n(n + 1)/2 + 1 distinct substrings.

Operations on languages

OperationDefinitionExample with L₁ = {a, ab}, L₂ = {b}
Union L₁ ∪ L₂Strings in either{a, ab, b}
Concatenation L₁L₂xy with x ∈ L₁, y ∈ L₂{ab, abb}
Power LᵏL concatenated k times; L⁰ = {ε}L₂³ = {bbb}
Kleene star L*L⁰ ∪ L¹ ∪ L² ∪ ...L₂* = {ε, b, bb, ...}
Complement L̄Σ* − L
Reverse LᴿEvery string reversedL₁ᴿ = {a, ba}

Note that ∅* = {ε} and ∅L = ∅ for any L, while {ε}L = L.

from itertools import product

def concat(A, B):
    return {x + y for x in A for y in B}

def power(L, k):
    result = {""}
    for _ in range(k):
        result = concat(result, L)
    return result

L1, L2 = {"a", "ab"}, {"b"}
print(sorted(concat(L1, L2)))
print(sorted(power(L1, 2)))
print("strings of length 3 over {0,1}:", len(list(product("01", repeat=3))))
star_up_to_3 = set().union(*(power(L1, k) for k in range(4)))
print("L1* up to 3 factors:", sorted(star_up_to_3, key=lambda s: (len(s), s)))
Output
['ab', 'abb']
['aa', 'aab', 'aba', 'abab']
strings of length 3 over {0,1}: 8
L1* up to 3 factors: ['', 'a', 'aa', 'ab', 'aaa', 'aab', 'aba', 'aaab', 'aaba', 'abaa', 'abab', 'aabab', 'abaab', 'ababa', 'ababab']

The Chomsky hierarchy

Languages are classified by the kind of machine that recognises them, or equivalently the kind of grammar that generates them. Each class strictly contains the one below it.

Recursively enumerable (type 0)Turing machineRecursive (decidable)TM that always haltsContext-sensitive (type 1)linear bounded automatonContext-free (type 2)pushdown automatonRegular (type 3)finite automaton
The Chomsky hierarchy (each class contains the ones below it)
TypeLanguage classGrammar rulesMachineMemory
3RegularA → aB or A → a (right-linear)Finite automaton (DFA/NFA)Only the current state
2Context-freeA → γ (one variable on the left)Pushdown automaton (PDA)A stack
1Context-sensitiveαAβ → αγβ, |γ| ≥ 1Linear bounded automatonTape as long as the input
0Recursively enumerableAny α → βTuring machineUnlimited tape

Typical examples:

  • Regular: strings with an even number of 0s; strings ending in 01; aⁿbᵐ.
  • Context-free but not regular: aⁿbⁿ; balanced parentheses; palindromes.
  • Context-sensitive but not context-free: aⁿbⁿcⁿ; ww (a string followed by a copy of itself).
  • Recursively enumerable but not recursive: the halting problem.
  • Not even recursively enumerable: the complement of the halting problem.

Recognising and deciding

A machine accepts a language L if it accepts exactly the strings of L. For Turing machines there is an important difference:

  • L is recursive (decidable) if some machine accepts every string in L and rejects (halts saying no) every string not in L.
  • L is recursively enumerable (semi-decidable) if some machine accepts every string in L, but on strings not in L it may run forever.

Finite automata and PDAs always finish after reading their input, so their languages are all decidable. The later lessons of this track build up the hierarchy from the bottom: finite automata and regular expressions, then context-free grammars and PDAs, then Turing machines and the limits of computation.

Practice questions

GATE-styleNAT · numerical answer

How many strings of length at most 3 are there over the alphabet {a, b}, including ε?

Show answer and explanationAnswer: 15

There are 2⁰ + 2¹ + 2² + 2³ = 1 + 2 + 4 + 8 = 15 strings of length 0 to 3.

GATE-styleNAT · numerical answer

L₁ = {ε, a} and L₂ = {a, aa}. How many distinct strings are in L₁L₂?

Show answer and explanationAnswer: 3

The four combinations give a, aa, aa and aaa. The string aa is produced twice, so L₁L₂ = {a, aa, aaa} has 3 strings.

GATE-styleMSQ · one or more correct answers

Which statements are true? (Select all that apply.)

Show answer and explanationAnswer: A, B, D

The star always contains ε (zero factors), even for ∅. {ε} contains the empty string, whereas ∅ contains no string. Concatenating with ∅ gives no pairs at all, so ∅L = ∅.

GATE-styleMCQ · one correct answer

Which machine model recognises exactly the context-free languages?

Show answer and explanationAnswer: B

Non-deterministic PDAs accept exactly the context-free languages. Deterministic PDAs accept only a proper subset (the deterministic CFLs), DFAs accept the regular languages, and LBAs the context-sensitive ones.

GATE-styleNAT · numerical answer

How many prefixes (including ε and the string itself) does the string abcab have?

Show answer and explanationAnswer: 6

A string of length n has n + 1 prefixes: ε, a, ab, abc, abca and abcab.