You expression is almost correct, the use of (\2|\3)
will cause issues, as these will match their content, but not their syntax. So certain examples should fail:
011101
Because the 11
can't be substituted by \3
, as the capture is empty at that point in time. So you'll have to duplicate the syntax:
^((00)*|(11)*|((10|01)(00|11)*(10|01))*)+$
Then there is some additional optimization you can do. Instead of repeating each internal group 0..* times and the out group 1..* times, you can remove the inner repetition and change the outer one to 0..*:
^(00|11|((10|01)(00|11)*(10|01))*$
Further simplification doesn't seem possible without resorting to tricks such as zero-width assertions, basically ensuring that you end up with an even number of 1's and and even number of 0's and ensuring that both are true:
^(?=0*((10*){2})*$)1*((01*){2})*$
Cheating a bit further, we already know by the capturing expression that we have an even number of 0's, so we can replace the look-ahead to just ensure we're on an even number of characters, there are no other characters to worry about:
^(?=(.{2})*$)1*((01*){2})*$
It's slightly shorter and notice that the first part of the expression is zero-width, the second part is not to ensure that we actually end up with a capture.
The final trick, completely moving away from a DFA, is to use balancing groups, since we're balancing 1's and 0's we know we're even when every odd 1 and 0 has been balanced by its even counterpart. It's not really simpler, but it does work and only needs one pass, like your example:
^((((?<z>0)|(?<o>1))|((?<-z>0)|(?<-o>1))))*(?(z)(?!))(?(o)(?!))$
([01][01])+
? \$\endgroup\$