Welcome again to the understanding regular expressions series, the previous part is located
here
if you missed it. Much like the dragon book you probably really can’t read these things out of order. Last time we ended with a really nice non deterministic finite automata, but in order to make it work in a deterministic fashion we will need to transform it. Again, a deterministic machine is the mirror image of a nondeterministic machine, it will achieve exactly the same thing but in a different fashion.
For reference, a nondeterministic finite automata:

One entry state

May contain epsilon transitions

One input may lead to several new states

One accepting state
It’s cousin the deterministic finite automata (DFA) has these properties:

One entry state

May not contain epsilon transition

One input may only lead to one state

May have several accepting states
The key thing about the DFA is that we can easily convert it to a table and run it through our very simple engine, giving us
very
nice runtimes of O(n). So how to convert this machine? The algorithm used here is called
closure
and it is very cool indeed. It is also used in a different variation for parser generation, which is why it’s almost a must to understand regular expressions before starting to truly understand parser generators. Here it is
Given a list of states the closure set is all the sets that can be reached without any input, including the input states themselves
The function will have an input of one or more states, and return a set of states which will at least contain the input states. In pseudo C# code it looks like this. I myself much prefer pseudocode that is much closer to real code – it leaves much less to the imagination of the reader.
Set<State> Closure(List<State> inputStates)
{
var output = new Set<State>();
output.AddRange(inputStates);
// Keeps states we are going to add later
while (true)
{
var statesToAdd = new Set<State>();
foreach (var state in output)
{
foreach (var edge in output.EdgesGoingOut)
{
if (edge.IsEpsilonEdge)
{
if (!output.Contains(statesToAdd))
{
statesToAdd.Add(edge.DestinationState);
}
}
}
}
if (!statesToAdd.Any())
break; // Exit loop if there are no states to add
output.UnionWith(statesToAdd); // Add all states to output
}
return output;
}
It’s helpful to visualize this using graphs. If we return to the regular expression
(ab)+bcd
, the corresponding NFA looks like this
If we are going to compute closure for state 0 we will traverse the edges to 1 and 3, but not to 5, because that edge goes the wrong way. Then there are nowhere else to go, since the remaining transitions require inputs “a” or “b” respectively. So the closure of state 0 is
{0, 1, 3}
. Note that the input state is always included in the closure list. I like to think of this as a flood fill, because it looks like that if you have many epsilon transitions. This new set is actually the start of our deterministic machine! Yay. Lets begin to draw the machine to watch it grow.
Our first state
We now need to introduce a new operation called
goto
. I know, goto is a bad word these days. The algorithm apologizes profoundly for the association. The goto algorithm is real easy.
Given a set of states and an input symbol find all the states reachable by traversing the edges that require the given input symbol. Then apply the closure algorithm on the output
Think of this algorithm as move one step then flood fill again. In C# pseudocode it looks like this
List<State> Goto(List<State> inputState, char inputCharacter)
{
var output = new Set<State>();
foreach (var state in inputState)
{
foreach (var edge in state.EdgesGoingOut)
{
if (edge.Label == inputCharacter)
{
output.Add(edge.DestinationState);
}
}
}
return Closure(output);
}
So, given our first closure set {0, 1, 3} lets apply Goto on it for every input character. Nothing will happen except for characters “a” and “b”. Lets explore character “a”. The only transition we get to cross is the one going from state 3 to 4. So 4 is the only result of the Goto. We now apply closure to it, which gives us the list {4, 5, 6, 0, 1, 3} when we follow every arrow which is an epsilon. The input for “b” is very similar and yields state set {2, 5, 6, 0, 1, 3}. The next step is adding our new states to the new diagram with the input we gave to the Goto as the edge label. Now the graph looks like this:
You then repeat this again. Applying Goto on the state set {4, 5, 6, 0, 1, 3} and input “a” gives us exacly the same result as previously. This means that this state has an edge going to itself. Applying Goto on the same states with input “b” gives a new state set that we haven’t seen before {2, 5, 6, 7, 0, 1, 3}. Lets add this to our graph:
If we apply the same algorithm to the state list {2, 5, 6, 0, 1, 3} we find that it will only yield sets that we have already added to the graph, but it will give us new transitions. The graph now looks like this:
There is one unexplored set to apply goto on {2, 5, 6, 7, 0, 1, 3}. Doing this with “a” gets a transition back to a previously known state. “b” gives a transition back to the same state list. “c” gives a transition to a brand new set containing only state 8. Our graph now looks like this:
Still one new set to explore! Only one input gives a new set “d” gives a set containing only state 9. State 9 is an accepting state, so we add this to the graph.
Applying goto on this new set gives us nothing at all. We are done. Almost by magic we have transformed our nondeteministic machine into a deterministic machine. The reader is invited to try the same input as on the nondeterministic variant. It will respond the same. There is really only one bit left to do, we need to make this into a table that will run with our regular expression engine.
Next part can be found
here