-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdfa.go
136 lines (119 loc) · 3.19 KB
/
dfa.go
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Package mindfa implements DFA minimization using Hopcroft's algorithm.
package mindfa
import (
"fmt"
"sort"
)
// Minimize takes a DFA representation, minimize it and returns the groups of
// the states. The states in a group has same behavior.
//
// The all of arguments represents a single DFA(before minimization). It has
// 0..nState states, 0..nSymbol input symbols and transitions as transition function.
//
// transition is a function takes state and symbol and returns the destination
// state. The states which is included in finals are accepting state. The order
// of the resulting partitions is not defined, but the order of the states in
// a partition is ascending.
//
// It uses Hopcroft's algorithm. The memory complexity is O(nState).
//
// The all numbers in finals must be in [0, nState), and two same values must not
// appear.
func Minimize(nState, nSymbol int, finals []int, transition func(state, symbol int) int) [][]int {
if len(finals) > nState {
panic(fmt.Sprintf("len(finals) should be less than or equal to nState: len(finals) = %d, nState = %d", len(finals), nState))
}
whole := make([]int, nState+max(len(finals), nState-len(finals)))
copy(whole, finals)
sort.Ints(whole[:len(finals)])
for i := 0; i < len(finals)-1; i++ {
if whole[i] == whole[i+1] {
panic(fmt.Sprintf("finals contains same two value: %d", whole[i]))
}
}
cmpl(whole[len(finals):nState], whole[:len(finals)], nState)
buf := whole[nState:]
partitions := [][]int{whole[:len(finals)], whole[len(finals):nState]}
// works is a set of the partition which has never tried to be split.
works := [][]int{whole[:len(finals)], whole[len(finals):nState]} // map?
for len(works) > 0 {
for c := 0; c < nSymbol; c++ {
for ip, pFrom := range partitions {
ip1, ip2 := 0, len(buf)-1
for _, state := range pFrom {
if includes(works[0], transition(state, c)) {
buf[ip1] = state
ip1++
} else {
buf[ip2] = state
ip2--
}
}
if ip1 == 0 || ip2 == len(buf)-1 {
continue
}
p1 := pFrom[:ip1]
copy(p1, buf[:ip1])
p2 := pFrom[ip1:]
for i := range p2 {
p2[i] = buf[len(buf)-1-i]
}
var split bool
for i, w := range works {
if &w[0] != &pFrom[0] {
continue
}
// Split works[i].
works = append(works, p2)
works[i] = p1
split = true
break
}
if !split {
if len(p1) < len(p2) {
works = append(works, p1)
} else {
works = append(works, p2)
}
}
partitions[ip] = p1
partitions = append(partitions, p2) // Don't worry, p2 is not iterated in the current loop.
}
}
// pseudo-shift
works[0] = works[len(works)-1]
works = works[:len(works)-1]
}
return partitions
}
// cmpl returns the complement set of a in (0..upper).
func cmpl(dst, a []int, upper int) {
var n, i int
for _, u := range a {
for ; n < u; n++ {
dst[i] = n
i++
}
n++
}
for ; n < upper; n++ {
dst[i] = n
i++
}
}
func includes(a []int, e int) bool {
if len(a) < 100 {
var i int
for ; i < len(a) && a[i] < e; i++ {
}
return i < len(a) && a[i] == e
}
i := sort.SearchInts(a, e)
return i < len(a) && a[i] == e
}
func max(a, b int) int {
if a > b {
return a
}
return b
}