Introduction to the theory of computation