On a possible non-recursive solution, as a possible improvement (per Reinderien's answer).
The general idea here is that if we build a tree of these strings in the obvious way (i.e. the children are one character extensions of their parents)way*, then the lexicographical ordering is nothing but a DFS on this tree.
*Let the root be the empty string, then its neighbours are the one character long strings for each character of the alphabet, and so on. For example on the alphabet ab with max_length = 2, the tree would look like the following:
<root>
|
+- a
| |
| +- aa
| |
| `- ab
|
`- b
|
+- ba
|
`- bb
Then we can sort of build the tree while doing the DFS like so:
def lexstrings(max_length=3, alphabet="ab"):
reversed_alphabet = list(reversed(alphabet))
nodes_to_visit = ['']
while nodes_to_visit:
current_node = nodes_to_visit.pop()
if len(current_node) > max_length:
continue
yield current_node
nodes_to_visit.extend(current_node + tc for tc in reversed_alphabet)