The product construction accepts exactly the strings on which the Boolean operation of the two languages is true full
This proof shows: correctness, termination.
Let M1=(Q1,Σ,δ1,s1,F1) and M2=(Q2,Σ,δ2,s2,F2) be complete DFAs over the same alphabet, with δi:Qi×Σ→Qi, and let op:{0,1}2→{0,1} be a Boolean function. Let P=(Q,Σ,δ,s,F) be the output of PRODUCT(M1,M2,op). Write δi∗ for the extension to strings: δi∗(q,ε)=q and δi∗(q,va)=δi(δi∗(q,v),a).
Lemma 1 (termination and reachability). The algorithm stops. Its set Q is the set of pairs (δ1∗(s1,w),δ2∗(s2,w)) for w∈Σ∗, and δ is defined on each pair of Q and each symbol.
Proof. A pair enters W only when it enters Q, at line L2 or line L7, and never leaves Q. So each pair of Q1×Q2 enters W at most once, the loop makes at most ∣Q1∣∣Q2∣ passes, and each pass does ∣Σ∣ steps. Each pair that enters Q is removed from W later, and then line L8 defines its move on each symbol.
Each pair of Q has the stated form: s=(δ1∗(s1,ε),δ2∗(s2,ε)), and if (p,q)=(δ1∗(s1,w),δ2∗(s2,w)), then line L6 adds (δ1(p,a),δ2(q,a))=(δ1∗(s1,wa),δ2∗(s2,wa)). Conversely, by induction on ∣w∣, the pair of w is in Q: true for ε, and if the pair of w is in Q, it is removed from W at some time, and line L7 puts the pair of wa in Q. □
Lemma 2 (the run of the product). For each w∈Σ∗, δ∗(s,w)=(δ1∗(s1,w),δ2∗(s2,w)).
Proof. By induction on ∣w∣. For w=ε both sides are s=(s1,s2). If the claim holds for w, with the pair (p,q), then δ∗(s,wa)=δ((p,q),a)=(δ1(p,a),δ2(q,a)) by line L6 and line L8, and this is (δ1∗(s1,wa),δ2∗(s2,wa)). □
Theorem. P is a complete DFA on its states, and for each w∈Σ∗,
w∈L(P)⟺op(w∈L(M1), w∈L(M2)). In particular, op = and gives L(M1)∩L(M2), op = or gives L(M1)∪L(M2), op(x,y)=x∧¬y gives L(M1)∖L(M2), and op = xor gives the symmetric difference.
Proof. By Lemma 1, each state of P has a move on each symbol, and the move is one pair of Q. By Lemma 2 and line L9, δ∗(s,w)∈F if and only if op(δ1∗(s1,w)∈F1,δ2∗(s2,w)∈F2), and δi∗(si,w)∈Fi if and only if w∈L(Mi). ■
Why completeness is needed. If M2 has no move on (q,a), then line L6 has no pair to build, and each string w that drives M2 into this gap is rejected by P, whatever the value of op. For op = or, a string in L(M1) that falls into the gap of M2 must be accepted, so the result is wrong. For op = and it is still correct, because such a string is not in L(M2). To use any other op, first add a dead state to each input.
Running time. By Lemma 1 there are N≤∣Q1∣∣Q2∣ passes, each with ∣Σ∣ lookups and one dictionary test per symbol, so the time is O(N∣Σ∣) with hashing, and O(∣Q1∣∣Q2∣∣Σ∣) in the worst case. The table has N∣Σ∣ entries.