Adaptive DFA edge tables

This change incorporates an adaptive growth strategy into the DFA edge tables to significantly reduce the amount of memory previously consumed by the `DFAState.edges` array. Multiple implementations of the new `EdgeMap` interface are used, with heavier (higher memory usage) instances only used when necessary.