Решение "грубой силы" состоит в том, чтобы проанализировать регулярное выражение в графе переходов конечного автомата, причем каждое состояние имеет список переходов, а каждый переход имеет связанный символ (символ) и следующее состояние. Вы можете использовать состояние без переходов, чтобы указать состояние терминала.
Затем просмотрите этот график, вспомнив строку, созданную переходами. Когда вы достигнете состояния терминала, напечатайте слово и уменьшите счетчик оставшихся слов, остановитесь, когда он достигнет нуля. Если возможно, что разные пути в графе приводят к одному и тому же слову, вам также нужно помнить любые уже выведенные слова и не печатать / уменьшать, если это же слово уже существует.
Обрабатывать пути в отсортированном порядке (так, чтобы более короткие пути предшествовали более длинным с тем же префиксом, т. Е. Согласно strcmp
в C). Это позволяет избежать зацикливания и дает нужный порядок.
Например, для регулярного выражения a*
(псевдокод):
state[0] = { {'a', 0}, {'\0', 1} };
state[1] = { }; // terminal state
paths = { { .state = 0, .string = "" } }; // set initial state
Вы начинаете с единственного пути, который у вас есть в состоянии 0
, и добавляете к нему (отдельно) оба перехода из состояния 0
, чтобы создать новые пути:
paths = { { .state = 1, .string = "" },
{ .state = 0, .string = "a" } };
Поскольку путь с пустой строкой упорядочен первым (из-за сортировки пустой строки перед любой непустой строкой), он обрабатывается первым, и, поскольку он находится в терминальном состоянии без переходов, он печатает пустую Количество строк и уменьшений слов. Затем вы берете другой путь и снова добавляете переходы из состояния 0
, заканчивающиеся на:
paths = { { .state = 1, .string = "a" },
{ .state = 0, .string = "aa" } };
и т.д.
(Отказ от ответственности: это полностью непроверено, вне моей головы, и могут быть угловые случаи, о которых я не задумывался. Также имейте в виду, что число путей взорвется для нетривиальных регулярных выражений.)