-
Notifications
You must be signed in to change notification settings - Fork 77
/
nfa.py
88 lines (81 loc) · 2.18 KB
/
nfa.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
* Execution: python nfa.py regexp text
*
* % python nfa.py "(A*B|AC)D" AAAABD
* true
*
* % python nfa.py "(A*B|AC)D" AAAAC
* false
*
* % python nfa.py "(a|(bc)*d)*" abcbcd
* true
*
* % python nfa.py "(a|(bc)*d)*" abcbcbcdaaaabcbcdaaaddd
* true
*
* Remarks
* -----------
* The following features are not supported:
* - The + operator
* - Multiway or
* - Metacharacters in the text
* - Character classes.
*
"""
from algs4.bag import Bag
from algs4.digraph import Digraph
from algs4.directed_dfs import DirectedDFS
from algs4.stack import Stack
class NFA:
def __init__(self, regexp):
ops = Stack()
M = len(regexp)
G = Digraph(M+1)
for i in range(M):
lp = i
if regexp[i] == "(" or regexp[i] == "|":
ops.push(i)
elif regexp[i] == ")":
op = ops.pop()
if regexp[op] == "|":
lp = ops.pop()
G.add_edge(lp, op+1)
G.add_edge(op, i)
else:
lp = op
if i < M-1 and regexp[i+1] == "*":
G.add_edge(lp, i+1)
G.add_edge(i+1, lp)
if regexp[i] in ("(", "*", ")"):
G.add_edge(i, i+1)
self.M = M
self.G = G
self.re = regexp
def recognizes(self, txt):
pc = Bag()
dfs = DirectedDFS(self.G, [0])
for v in range(self.G.V):
if dfs.marked(v):
pc.add(v)
for i in range(len(txt)):
match = Bag()
for v in pc:
if v < self.M:
if self.re[v] == txt[i] or self.re[v] == ".":
match.add(v+1)
pc = Bag()
dfs = DirectedDFS(self.G, match)
for v in range(self.G.V):
if dfs.marked(v):
pc.add(v)
for v in pc:
if v == self.M:
return True
return False
def char_at(self, s, d):
return ord(s[d])
if __name__ == "__main__":
import sys
pattern, txt = sys.argv[1], sys.argv[2]
nfa = NFA(pattern)
print(nfa.recognizes(txt))