Python, 34413453 bytes
import random
def skip():pass
class State:
states = []
def __init__(self, accepting=False):
self.n = len(State.states)
State.states.append(self)
self.accepting = accepting
self.transitions = {}
def __repr__(self):
return f'q_{self.n}'
def add(self, c, s):
self.transitions.setdefault(c, set()).add(s)
def __eq__(self, o):
return self.n == o.n
def __hash__(self):
return hash((State, self.n))
def epsilon_helper(self):
z = {self}
if '' in self.transitions:
for s in self.transitions.pop(''):
z.update(s.epsilon_helper())
self.epsilon_helper = lambda:z
return z
def remove_epsilon(self):
self.remove_epsilon = skip
for a in self.epsilon_helper():
a.remove_epsilon()
for x, S in a.transitions.items():
for b in list(S):
b.remove_epsilon()
for c in b.epsilon_helper():
self.add(x, c)
c.remove_epsilon()
def freeze(self):
self.freeze = lambda:None
for c, s in self.transitions.items():
for x in s:
x.freeze()
self.transitions[c] = frozenset(s)
def count_accepting(self):
x = self.accepting + sum(s.count_accepting() for S in self.transitions.values() for s in S)
self.count_accepting = lambda:x
return x
def accept(self, w):
if w == '': return self.accepting
return any(s.accept(w[1:]) for s in self.transitions.get(w[0],()))
def pick(self, i):
if self.accepting:
if i == 0: return ''
i -= 1
for j, s in self.transitions.items():
s = min(s)
x = s.count_accepting()
if i < x:
return j + s.pick(i)
i -= x
raise IndexError
def levenshtein(w, n):
A = set(w)
W = len(w)
states = [[State(j <= i) for _j in w] + [Staterange(Truelen(w),-1,-1)] for _i in range(n+1n,-1,-1)]
for i, c in enumerate(w):
for j in range(n):
s = states[j][i]
for x in A:
s.add(x, states[j+1][i])
if x != c:
s.add(x, states[j+1][i+1])
s.add('', states[j+1][i+1])
s.add(c, states[j][i+1])
states[n][i].add(c, states[n][i+1])
for j in range(n):
s = states[j][W]
for x in A:
s.add(x, states[j+1][W])
s = states[0][0]
s.remove_epsilon()
s.freeze()
return s
def union(states):
d = {}
accepting = False
for s in states:
for c, S in s.transitions.items():
d[c] = d.get(c, frozenset()) | S
if s.accepting:
accepting = True
return d, accepting
def difference(q0, q1, D = None):
if D is None:
return difference(frozenset([q0]), frozenset([q1]), {})
if (q0, q1) in D:
return D[q0, q1]
d0, a0 = union(q0)
d1, a1 = union(q1)
Q0 = State(a0 and not a1)
D[q0, q1] = Q0
for i in set(d0) | set(d1):
Q0.transitions[i] = frozenset([difference(d0.get(i, frozenset()), d1.get(i, frozenset()), D)])
return Q0
def exact(w, n):
if n == 0: raise ValueError
return difference(levenshtein(w, n), levenshtein(w, n-1))
def f(w, n):
s = exact(w, n)
return s.pick(random.randrange(s.count_accepting()))
Attempt This Online!Attempt This Online!