Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from itertools import *
- from functools import *
- from operator import *
- from copy import *
- class NFA:
- def __init__(self):
- self.trans : list[dict[str, set[int]]] = []
- self.rtrans : list[dict[str, set[int]]] = []
- self.start = 1 << 31
- self.accept = 1 << 31
- def state_count(self) -> int:
- return len(self.trans)
- def add_state(self) -> int:
- state = self.state_count()
- self.trans.append({})
- self.rtrans.append({})
- return state
- def add_transition(self, p : int, c : str, q : int):
- if c in self.trans[p]:
- self.trans[p][c].add(q)
- else:
- self.trans[p][c] = set([q])
- if c in self.rtrans[q]:
- self.rtrans[q][c].add(p)
- else:
- self.rtrans[q][c] = set([p])
- return self
- def remove_transition(self, p : int, c : str, q : int):
- self.trans[p][c].remove(q)
- if len(self.trans[p][c]) == 0:
- del self.trans[p][c]
- self.rtrans[q][c].remove(p)
- if len(self.rtrans[q][c]) == 0:
- del self.rtrans[q][c]
- return self
- def pop_state(self):
- self.trans.pop()
- self.rtrans.pop()
- return self
- def disjoin_state(self, q : int):
- for k in self.trans[q].copy():
- for s in self.trans[q][k].copy():
- self.remove_transition(q, k, s)
- for k in self.rtrans[q].copy():
- for r in self.rtrans[q][k].copy():
- self.remove_transition(r, k, q)
- return self
- # merges state p to q, transfering all of the transitions, and disjoining p
- def merge_states(self, p : int, q : int):
- for k in self.trans[p]:
- for s in self.trans[p][k]:
- self.add_transition(q, k, s)
- for k in self.rtrans[p]:
- for r in self.rtrans[p][k]:
- self.add_transition(r, k, q)
- self.disjoin_state(p)
- return self
- # returns an automaton that has the same states as self and other, only other's states are shifted by self.state_count()
- def join(self, other):
- otrans = deepcopy(other.trans)
- for d in otrans:
- for k in d:
- d[k] = set(map(partial(add, self.state_count()), d[k]))
- ortrans = deepcopy(other.rtrans)
- for d in ortrans:
- for k in d:
- d[k] = set(map(partial(add, self.state_count()), d[k]))
- result = NFA()
- result.trans = self.trans + otrans
- result.rtrans = self.rtrans + ortrans
- return result;
- # vvv these functions aren't quite right when concatenating two closures or alternating with any closrue
- # fortunately no regexps in this task require doing that
- def concat(self, other):
- result = self.join(other)
- result.merge_states(other.start + self.state_count(), self.accept)
- result.set_start(self.start)
- if other.accept != other.start:
- result.set_accept(other.accept + self.state_count())
- else:
- result.set_accept(self.accept)
- return result
- def alter(self, other):
- result = self.join(other)
- result.merge_states(other.start + self.state_count(), self.start)
- result.merge_states(other.accept + self.state_count(), self.accept)
- result.set_start(self.start)
- result.set_accept(self.accept)
- result.pop_state().pop_state()
- return result
- def closure(self):
- result = deepcopy(self)
- result.merge_states(result.accept, result.start)
- result.set_accept(result.start)
- result.pop_state()
- return result
- def repeat(self, n : int):
- result = deepcopy(self)
- for i in range(1, n):
- result = result.concat(self)
- return result
- def char(c : str):
- nfa = NFA()
- p = nfa.add_state()
- q = nfa.add_state()
- nfa.set_start(p)
- nfa.set_accept(q)
- nfa.add_transition(p, c, q)
- return nfa
- def set_start(self, s : int):
- self.start = s
- return self
- def set_accept(self, f : int):
- self.accept = f
- return self
- def delta(self, p : int, c : str) -> set[int]:
- if c not in self.trans[p]:
- return set()
- return self.trans[p][c]
- def simulate(self, inputstr : str) -> int:
- pset = dict({ self.start : 1 })
- for c in inputstr:
- qset = {}
- for p in pset:
- for q in self.delta(p, c):
- if q in qset:
- qset[q] += pset[p]
- else:
- qset[q] = pset[p]
- pset = qset
- result = qset[self.accept]
- return result
- def __repr__(self):
- return f'start = {self.start}\naccept = {self.accept}\ntrans = {self.trans}\nrtrans = {self.rtrans}\n'
- def mk_nfa(numseq : list[int]):
- head = numseq[0]
- tail = numseq[1:]
- ok_nfa = NFA.char('?').alter(NFA.char('.'))
- many_ok_nfa = ok_nfa.concat(ok_nfa.closure())
- damaged_nfa = NFA.char('?').alter(NFA.char('#'))
- result = ok_nfa.closure()
- result = result.concat(damaged_nfa.repeat(head))
- for i in tail:
- result = result.concat(many_ok_nfa)
- result = result.concat(damaged_nfa.repeat(i))
- result = result.concat(ok_nfa.closure())
- return result
- total = 0
- for line in filterfalse(str.isspace, open(0).readlines()):
- [inputstr, numseq] = line.split()
- numseq = list(map(int, numseq.split(',')))
- nfa = mk_nfa(numseq)
- total += nfa.simulate(inputstr)
- print(total)
- for line in filterfalse(str.isspace, open(0).readlines()):
- [inputstr, numseq] = line.split()
- numseq = list(map(int, numseq.split(',')))
- nfa = mk_nfa(numseq * 5)
- total += nfa.simulate('?'.join(repeat(inputstr, 5)))
- print(total)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement