algorithms 0.3.0-jruby
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +172 -0
- data/Manifest +43 -0
- data/README.markdown +93 -0
- data/Rakefile +31 -0
- data/algorithms.gemspec +33 -0
- data/benchmarks/deque.rb +17 -0
- data/benchmarks/sorts.rb +34 -0
- data/benchmarks/treemaps.rb +51 -0
- data/ext/containers/deque/deque.c +247 -0
- data/ext/containers/deque/extconf.rb +4 -0
- data/ext/containers/rbtree_map/extconf.rb +4 -0
- data/ext/containers/rbtree_map/rbtree.c +498 -0
- data/ext/containers/splaytree_map/extconf.rb +4 -0
- data/ext/containers/splaytree_map/splaytree.c +419 -0
- data/lib/algorithms.rb +68 -0
- data/lib/algorithms/search.rb +84 -0
- data/lib/algorithms/sort.rb +238 -0
- data/lib/containers/deque.rb +171 -0
- data/lib/containers/heap.rb +486 -0
- data/lib/containers/kd_tree.rb +110 -0
- data/lib/containers/priority_queue.rb +113 -0
- data/lib/containers/queue.rb +68 -0
- data/lib/containers/rb_tree_map.rb +398 -0
- data/lib/containers/splay_tree_map.rb +269 -0
- data/lib/containers/stack.rb +67 -0
- data/lib/containers/suffix_array.rb +68 -0
- data/lib/containers/trie.rb +182 -0
- data/spec/deque_gc_mark_spec.rb +18 -0
- data/spec/deque_spec.rb +108 -0
- data/spec/heap_spec.rb +126 -0
- data/spec/kd_expected_out.txt +10000 -0
- data/spec/kd_test_in.txt +10000 -0
- data/spec/kd_tree_spec.rb +34 -0
- data/spec/map_gc_mark_spec.rb +27 -0
- data/spec/priority_queue_spec.rb +75 -0
- data/spec/queue_spec.rb +61 -0
- data/spec/rb_tree_map_spec.rb +123 -0
- data/spec/search_spec.rb +28 -0
- data/spec/sort_spec.rb +28 -0
- data/spec/splay_tree_map_spec.rb +106 -0
- data/spec/stack_spec.rb +60 -0
- data/spec/suffix_array_spec.rb +40 -0
- data/spec/trie_spec.rb +59 -0
- metadata +122 -0
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'containers/stack'
|
2
|
+
=begin rdoc
|
3
|
+
A SplayTreeMap is a map that is stored in ascending order of its keys, determined by applying
|
4
|
+
the function <=> to compare keys. No duplicate values for keys are allowed, so new values of a key
|
5
|
+
overwrites the old value of the key.
|
6
|
+
|
7
|
+
A major advantage of SplayTreeMap over a Hash is the fact that keys are stored in order and can thus be
|
8
|
+
iterated over in order. Also, Splay Trees are self-optimizing as recently accessed nodes stay near
|
9
|
+
the root and are easily re-accessed later. Splay Trees are also more simply implemented than Red-Black
|
10
|
+
trees.
|
11
|
+
|
12
|
+
Splay trees have amortized O(log n) performance for most methods, but are O(n) worst case. This happens
|
13
|
+
when keys are added in sorted order, causing the tree to have a height of the number of items added.
|
14
|
+
|
15
|
+
=end
|
16
|
+
class Containers::RubySplayTreeMap
|
17
|
+
include Enumerable
|
18
|
+
|
19
|
+
Node = Struct.new(:key, :value, :left, :right)
|
20
|
+
|
21
|
+
# Create and initialize a new empty SplayTreeMap.
|
22
|
+
def initialize
|
23
|
+
@size = 0
|
24
|
+
clear
|
25
|
+
end
|
26
|
+
|
27
|
+
# Insert an item with an associated key into the SplayTreeMap, and returns the item inserted
|
28
|
+
#
|
29
|
+
# Complexity: amortized O(log n)
|
30
|
+
#
|
31
|
+
# map = Containers::SplayTreeMap.new
|
32
|
+
# map.push("MA", "Massachusetts") #=> "Massachusetts"
|
33
|
+
# map.get("MA") #=> "Massachusetts"
|
34
|
+
def push(key, value)
|
35
|
+
if @root.nil?
|
36
|
+
@root = Node.new(key, value, nil, nil)
|
37
|
+
@size = 1
|
38
|
+
return value
|
39
|
+
end
|
40
|
+
splay(key)
|
41
|
+
|
42
|
+
cmp = (key <=> @root.key)
|
43
|
+
if cmp == 0
|
44
|
+
@root.value = value
|
45
|
+
return value
|
46
|
+
end
|
47
|
+
node = Node.new(key, value, nil, nil)
|
48
|
+
if cmp < 1
|
49
|
+
node.left = @root.left
|
50
|
+
node.right = @root
|
51
|
+
@root.left = nil
|
52
|
+
else
|
53
|
+
node.right = @root.right
|
54
|
+
node.left = @root
|
55
|
+
@root.right = nil
|
56
|
+
end
|
57
|
+
@root = node
|
58
|
+
@size += 1
|
59
|
+
value
|
60
|
+
end
|
61
|
+
alias_method :[]=, :push
|
62
|
+
|
63
|
+
# Return the number of items in the SplayTreeMap.
|
64
|
+
#
|
65
|
+
# map = Containers::SplayTreeMap.new
|
66
|
+
# map.push("MA", "Massachusetts")
|
67
|
+
# map.push("GA", "Georgia")
|
68
|
+
# map.size #=> 2
|
69
|
+
def size
|
70
|
+
@size
|
71
|
+
end
|
72
|
+
|
73
|
+
# Remove all elements from the SplayTreeMap
|
74
|
+
#
|
75
|
+
# Complexity: O(1)
|
76
|
+
#
|
77
|
+
def clear
|
78
|
+
@root = nil
|
79
|
+
@size = 0
|
80
|
+
@header = Node.new(nil, nil, nil, nil)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return the height of the tree structure in the SplayTreeMap.
|
84
|
+
#
|
85
|
+
# Complexity: O(log n)
|
86
|
+
#
|
87
|
+
# map = Containers::SplayTreeMap.new
|
88
|
+
# map.push("MA", "Massachusetts")
|
89
|
+
# map.push("GA", "Georgia")
|
90
|
+
# map.height #=> 2
|
91
|
+
def height
|
92
|
+
height_recursive(@root)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Return true if key is found in the SplayTreeMap, false otherwise.
|
96
|
+
#
|
97
|
+
# Complexity: amortized O(log n)
|
98
|
+
#
|
99
|
+
# map = Containers::SplayTreeMap.new
|
100
|
+
# map["MA"] = "Massachusetts"
|
101
|
+
# map["GA"] = "Georgia"
|
102
|
+
# map.has_key?("GA") #=> true
|
103
|
+
# map.has_key?("DE") #=> false
|
104
|
+
def has_key?(key)
|
105
|
+
!get(key).nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return the item associated with the key, or nil if none found.
|
109
|
+
#
|
110
|
+
# Complexity: amortized O(log n)
|
111
|
+
#
|
112
|
+
# map = Containers::SplayTreeMap.new
|
113
|
+
# map.push("MA", "Massachusetts")
|
114
|
+
# map.push("GA", "Georgia")
|
115
|
+
# map.get("GA") #=> "Georgia"
|
116
|
+
def get(key)
|
117
|
+
return nil if @root.nil?
|
118
|
+
|
119
|
+
splay(key)
|
120
|
+
(@root.key <=> key) == 0 ? @root.value : nil
|
121
|
+
end
|
122
|
+
alias_method :[], :get
|
123
|
+
|
124
|
+
# Return the smallest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
|
125
|
+
#
|
126
|
+
# Complexity: amortized O(log n)
|
127
|
+
#
|
128
|
+
# map = Containers::SplayTreeMap.new
|
129
|
+
# map["MA"] = "Massachusetts"
|
130
|
+
# map["GA"] = "Georgia"
|
131
|
+
# map.min #=> ["GA", "Georgia"]
|
132
|
+
def min
|
133
|
+
return nil if @root.nil?
|
134
|
+
n = @root
|
135
|
+
while n.left
|
136
|
+
n = n.left
|
137
|
+
end
|
138
|
+
splay(n.key)
|
139
|
+
return [n.key, n.value]
|
140
|
+
end
|
141
|
+
|
142
|
+
# Return the largest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
|
143
|
+
#
|
144
|
+
# Complexity: amortized O(log n)
|
145
|
+
#
|
146
|
+
# map = Containers::SplayTreeMap.new
|
147
|
+
# map["MA"] = "Massachusetts"
|
148
|
+
# map["GA"] = "Georgia"
|
149
|
+
# map.max #=> ["MA", "Massachusetts"]
|
150
|
+
def max
|
151
|
+
return nil if @root.nil?
|
152
|
+
n = @root
|
153
|
+
while n.right
|
154
|
+
n = n.right
|
155
|
+
end
|
156
|
+
splay(n.key)
|
157
|
+
return [n.key, n.value]
|
158
|
+
end
|
159
|
+
|
160
|
+
# Deletes the item and key if it's found, and returns the item. Returns nil
|
161
|
+
# if key is not present.
|
162
|
+
#
|
163
|
+
# Complexity: amortized O(log n)
|
164
|
+
#
|
165
|
+
# map = Containers::SplayTreeMap.new
|
166
|
+
# map["MA"] = "Massachusetts"
|
167
|
+
# map["GA"] = "Georgia"
|
168
|
+
# map.delete("GA") #=> "Georgia"
|
169
|
+
# map.delete("DE") #=> nil
|
170
|
+
def delete(key)
|
171
|
+
return nil if @root.nil?
|
172
|
+
deleted = nil
|
173
|
+
splay(key)
|
174
|
+
if (key <=> @root.key) == 0 # The key exists
|
175
|
+
deleted = @root.value
|
176
|
+
if @root.left.nil?
|
177
|
+
@root = @root.right
|
178
|
+
else
|
179
|
+
x = @root.right
|
180
|
+
@root = @root.left
|
181
|
+
splay(key)
|
182
|
+
@root.right = x
|
183
|
+
end
|
184
|
+
end
|
185
|
+
deleted
|
186
|
+
end
|
187
|
+
|
188
|
+
# Iterates over the SplayTreeMap in ascending order. Uses an iterative, not recursive, approach.
|
189
|
+
def each
|
190
|
+
return nil unless @root
|
191
|
+
stack = Containers::Stack.new
|
192
|
+
cursor = @root
|
193
|
+
loop do
|
194
|
+
if cursor
|
195
|
+
stack.push(cursor)
|
196
|
+
cursor = cursor.left
|
197
|
+
else
|
198
|
+
unless stack.empty?
|
199
|
+
cursor = stack.pop
|
200
|
+
yield(cursor.key, cursor.value)
|
201
|
+
cursor = cursor.right
|
202
|
+
else
|
203
|
+
break
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Moves a key to the root, updating the structure in each step.
|
210
|
+
def splay(key)
|
211
|
+
l, r = @header, @header
|
212
|
+
t = @root
|
213
|
+
@header.left, @header.right = nil, nil
|
214
|
+
|
215
|
+
loop do
|
216
|
+
if (key <=> t.key) == -1
|
217
|
+
break unless t.left
|
218
|
+
if (key <=> t.left.key) == -1
|
219
|
+
y = t.left
|
220
|
+
t.left = y.right
|
221
|
+
y.right = t
|
222
|
+
t = y
|
223
|
+
break unless t.left
|
224
|
+
end
|
225
|
+
r.left = t
|
226
|
+
r = t
|
227
|
+
t = t.left
|
228
|
+
elsif (key <=> t.key) == 1
|
229
|
+
break unless t.right
|
230
|
+
if (key <=> t.right.key) == 1
|
231
|
+
y = t.right
|
232
|
+
t.right = y.left
|
233
|
+
y.left = t
|
234
|
+
t = y
|
235
|
+
break unless t.right
|
236
|
+
end
|
237
|
+
l.right = t
|
238
|
+
l = t
|
239
|
+
t = t.right
|
240
|
+
else
|
241
|
+
break
|
242
|
+
end
|
243
|
+
end
|
244
|
+
l.right = t.left
|
245
|
+
r.left = t.right
|
246
|
+
t.left = @header.right
|
247
|
+
t.right = @header.left
|
248
|
+
@root = t
|
249
|
+
end
|
250
|
+
private :splay
|
251
|
+
|
252
|
+
# Recursively determine height
|
253
|
+
def height_recursive(node)
|
254
|
+
return 0 if node.nil?
|
255
|
+
|
256
|
+
left_height = 1 + height_recursive(node.left)
|
257
|
+
right_height = 1 + height_recursive(node.right)
|
258
|
+
|
259
|
+
left_height > right_height ? left_height : right_height
|
260
|
+
end
|
261
|
+
private :height_recursive
|
262
|
+
end
|
263
|
+
|
264
|
+
begin
|
265
|
+
require 'CSplayTreeMap'
|
266
|
+
Containers::SplayTreeMap = Containers::CSplayTreeMap
|
267
|
+
rescue LoadError # C Version could not be found, try ruby version
|
268
|
+
Containers::SplayTreeMap = Containers::RubySplayTreeMap
|
269
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'containers/deque'
|
2
|
+
=begin rdoc
|
3
|
+
A Stack is a container that keeps elements in a last-in first-out (LIFO) order. There are many
|
4
|
+
uses for stacks, including prefix-infix-postfix conversion and backtracking problems.
|
5
|
+
|
6
|
+
This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
|
7
|
+
|
8
|
+
=end
|
9
|
+
class Containers::Stack
|
10
|
+
include Enumerable
|
11
|
+
# Create a new stack. Takes an optional array argument to initialize the stack.
|
12
|
+
#
|
13
|
+
# s = Containers::Stack.new([1, 2, 3])
|
14
|
+
# s.pop #=> 3
|
15
|
+
# s.pop #=> 2
|
16
|
+
def initialize(ary=[])
|
17
|
+
@container = Containers::Deque.new(ary)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the next item from the stack but does not remove it.
|
21
|
+
#
|
22
|
+
# s = Containers::Stack.new([1, 2, 3])
|
23
|
+
# s.next #=> 3
|
24
|
+
# s.size #=> 3
|
25
|
+
def next
|
26
|
+
@container.back
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds an item to the stack.
|
30
|
+
#
|
31
|
+
# s = Containers::Stack.new([1])
|
32
|
+
# s.push(2)
|
33
|
+
# s.pop #=> 2
|
34
|
+
# s.pop #=> 1
|
35
|
+
def push(obj)
|
36
|
+
@container.push_back(obj)
|
37
|
+
end
|
38
|
+
alias_method :<<, :push
|
39
|
+
|
40
|
+
# Removes the next item from the stack and returns it.
|
41
|
+
#
|
42
|
+
# s = Containers::Stack.new([1, 2, 3])
|
43
|
+
# s.pop #=> 3
|
44
|
+
# s.size #=> 2
|
45
|
+
def pop
|
46
|
+
@container.pop_back
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return the number of items in the stack.
|
50
|
+
#
|
51
|
+
# s = Containers::Stack.new([1, 2, 3])
|
52
|
+
# s.size #=> 3
|
53
|
+
def size
|
54
|
+
@container.size
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if the stack is empty, false otherwise.
|
58
|
+
def empty?
|
59
|
+
@container.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Iterate over the Stack in LIFO order.
|
63
|
+
def each(&block)
|
64
|
+
@container.each_backward(&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
A suffix array enables fast substring search of a given string. An array of all possible substrings
|
3
|
+
is constructed and stored, and a binary search is then done to find a desired substring among those
|
4
|
+
stored. While more storage (and thus memory) is needed to create the SuffixArray, the advantage is
|
5
|
+
that substrings can be found in O(m log n) time, where m is the length of the substring to search for
|
6
|
+
and n is the total number of substrings.
|
7
|
+
|
8
|
+
=end
|
9
|
+
class Containers::SuffixArray
|
10
|
+
# Creates a new SuffixArray with a given string. Object of any class implementing a #to_s method can
|
11
|
+
# be passed in, such as integers.
|
12
|
+
#
|
13
|
+
# Complexity: O(n^2 log n)
|
14
|
+
#
|
15
|
+
# s_array = Containers::SuffixArray("abracadabra")
|
16
|
+
# s_array["abra"] #=> true
|
17
|
+
#
|
18
|
+
# number = Containers::SuffixArray(1234567)
|
19
|
+
# number[1] #=> true
|
20
|
+
# number[13] #=> false
|
21
|
+
def initialize(string)
|
22
|
+
string = string.to_s
|
23
|
+
raise ArgumentError, "SuffixArray needs to be initialized with a non-empty string" if string.empty?
|
24
|
+
@original_string = string
|
25
|
+
@suffixes = []
|
26
|
+
string.length.times do |i|
|
27
|
+
@suffixes << string[i..-1]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sort the suffixes in ascending order
|
31
|
+
@suffixes.sort! { |x, y| x <=> y }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns true if the substring occurs in the string, false otherwise.
|
35
|
+
#
|
36
|
+
# Complexity: O(m + log n)
|
37
|
+
#
|
38
|
+
# s_array = Containers::SuffixArray.new("abracadabra")
|
39
|
+
# s_array.has_substring?("a") #=> true
|
40
|
+
# s_array.has_substring?("abra") #=> true
|
41
|
+
# s_array.has_substring?("abracadabra") #=> true
|
42
|
+
# s_array.has_substring?("acadabra") #=> true
|
43
|
+
# s_array.has_substring?("adabra") #=> true
|
44
|
+
# s_array.has_substring?("bra") #=> true
|
45
|
+
# s_array.has_substring?("bracadabra") #=> true
|
46
|
+
# s_array.has_substring?("cadabra") #=> true
|
47
|
+
# s_array.has_substring?("dabra") #=> true
|
48
|
+
# s_array.has_substring?("ra") #=> true
|
49
|
+
# s_array.has_substring?("racadabra") #=> true
|
50
|
+
# s_array.has_substring?("nope") #=> false
|
51
|
+
def has_substring?(substring)
|
52
|
+
substring = substring.to_s
|
53
|
+
return false if substring.empty?
|
54
|
+
substring_length = substring.length-1
|
55
|
+
l, r = 0, @suffixes.size-1
|
56
|
+
while(l <= r)
|
57
|
+
mid = (l + r) / 2
|
58
|
+
suffix = @suffixes[mid][0..substring_length]
|
59
|
+
case substring <=> suffix
|
60
|
+
when 0 then return true
|
61
|
+
when 1 then l = mid + 1
|
62
|
+
when -1 then r = mid - 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
alias_method :[], :has_substring?
|
68
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
A Trie is a data structure that stores key value pairs in a tree-like fashion. It allows
|
3
|
+
O(m) lookup speed, where m is the length of the key searched, and has no chance of collisions,
|
4
|
+
unlike hash tables. Because of its nature, search misses are quickly detected.
|
5
|
+
|
6
|
+
Tries are often used for longest prefix algorithms, wildcard matching, and can be used to
|
7
|
+
implement a radix sort.
|
8
|
+
|
9
|
+
This implemention is based on a Ternary Search Tree.
|
10
|
+
=end
|
11
|
+
class Containers::Trie
|
12
|
+
# Create a new, empty Trie.
|
13
|
+
#
|
14
|
+
# t = Containers::Trie.new
|
15
|
+
# t["hello"] = "world"
|
16
|
+
# t["hello] #=> "world"
|
17
|
+
def initialize
|
18
|
+
@root = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Adds a key, value pair to the Trie, and returns the value if successful. The to_s method is
|
22
|
+
# called on the parameter to turn it into a string.
|
23
|
+
#
|
24
|
+
# Complexity: O(m)
|
25
|
+
#
|
26
|
+
# t = Containers::Trie.new
|
27
|
+
# t["hello"] = "world"
|
28
|
+
# t.push("hello", "world") # does the same thing
|
29
|
+
# t["hello"] #=> "world"
|
30
|
+
# t[1] = 1
|
31
|
+
# t[1] #=> 1
|
32
|
+
def push(key, value)
|
33
|
+
key = key.to_s
|
34
|
+
return nil if key.empty?
|
35
|
+
@root = push_recursive(@root, key, 0, value)
|
36
|
+
value
|
37
|
+
end
|
38
|
+
alias_method :[]=, :push
|
39
|
+
|
40
|
+
# Returns true if the key is contained in the Trie.
|
41
|
+
#
|
42
|
+
# Complexity: O(m) worst case
|
43
|
+
#
|
44
|
+
def has_key?(key)
|
45
|
+
key = key.to_s
|
46
|
+
return false if key.empty?
|
47
|
+
!(get_recursive(@root, key, 0).nil?)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the value of the desired key, or nil if the key doesn't exist.
|
51
|
+
#
|
52
|
+
# Complexity: O(m) worst case
|
53
|
+
#
|
54
|
+
# t = Containers::Trie.new
|
55
|
+
# t.get("hello") = "world"
|
56
|
+
# t.get("non-existant") #=> nil
|
57
|
+
def get(key)
|
58
|
+
key = key.to_s
|
59
|
+
return nil if key.empty?
|
60
|
+
node = get_recursive(@root, key, 0)
|
61
|
+
node ? node.last : nil
|
62
|
+
end
|
63
|
+
alias_method :[], :get
|
64
|
+
|
65
|
+
# Returns the longest key that has a prefix in common with the parameter string. If
|
66
|
+
# no match is found, the blank string "" is returned.
|
67
|
+
#
|
68
|
+
# Complexity: O(m) worst case
|
69
|
+
#
|
70
|
+
# t = Containers::Trie.new
|
71
|
+
# t.push("Hello", "World")
|
72
|
+
# t.push("Hello, brother", "World")
|
73
|
+
# t.push("Hello, bob", "World")
|
74
|
+
# t.longest_prefix("Hello, brandon") #=> "Hello"
|
75
|
+
# t.longest_prefix("Hel") #=> ""
|
76
|
+
# t.longest_prefix("Hello") #=> "Hello"
|
77
|
+
def longest_prefix(string)
|
78
|
+
string = string.to_s
|
79
|
+
return nil if string.empty?
|
80
|
+
len = prefix_recursive(@root, string, 0)
|
81
|
+
string[0...len]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a sorted array containing strings that match the parameter string. The wildcard
|
85
|
+
# characters that match any character are '*' and '.' If no match is found, an empty
|
86
|
+
# array is returned.
|
87
|
+
#
|
88
|
+
# Complexity: O(n) worst case
|
89
|
+
#
|
90
|
+
# t = Containers::Trie.new
|
91
|
+
# t.push("Hello", "World")
|
92
|
+
# t.push("Hilly", "World")
|
93
|
+
# t.push("Hello, bob", "World")
|
94
|
+
# t.wildcard("H*ll.") #=> ["Hello", "Hilly"]
|
95
|
+
# t.wildcard("Hel") #=> []
|
96
|
+
def wildcard(string)
|
97
|
+
string = string.to_s
|
98
|
+
return nil if string.empty?
|
99
|
+
ary = []
|
100
|
+
ary << wildcard_recursive(@root, string, 0, "")
|
101
|
+
ary.flatten.compact.sort
|
102
|
+
end
|
103
|
+
|
104
|
+
class Node # :nodoc: all
|
105
|
+
attr_accessor :left, :mid, :right, :char, :value, :end
|
106
|
+
|
107
|
+
def initialize(char, value)
|
108
|
+
@char = char
|
109
|
+
@value = value
|
110
|
+
@left = @mid = @right = nil
|
111
|
+
@end = false
|
112
|
+
end
|
113
|
+
|
114
|
+
def last?
|
115
|
+
@end == true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def wildcard_recursive(node, string, index, prefix)
|
120
|
+
return nil if node.nil? || index == string.length
|
121
|
+
arr = []
|
122
|
+
char = string[index]
|
123
|
+
if (char.chr == "*" || char.chr == "." || char < node.char)
|
124
|
+
arr << wildcard_recursive(node.left, string, index, prefix)
|
125
|
+
end
|
126
|
+
if (char.chr == "*" || char.chr == "." || char > node.char)
|
127
|
+
arr << wildcard_recursive(node.right, string, index, prefix)
|
128
|
+
end
|
129
|
+
if (char.chr == "*" || char.chr == "." || char == node.char)
|
130
|
+
arr << "#{prefix}#{node.char.chr}" if node.last?
|
131
|
+
arr << wildcard_recursive(node.mid, string, index+1, prefix + node.char.chr)
|
132
|
+
end
|
133
|
+
arr
|
134
|
+
end
|
135
|
+
|
136
|
+
def prefix_recursive(node, string, index)
|
137
|
+
return 0 if node.nil? || index == string.length
|
138
|
+
len = 0
|
139
|
+
rec_len = 0
|
140
|
+
char = string[index]
|
141
|
+
if (char < node.char)
|
142
|
+
rec_len = prefix_recursive(node.left, string, index)
|
143
|
+
elsif (char > node.char)
|
144
|
+
rec_len = prefix_recursive(node.right, string, index)
|
145
|
+
else
|
146
|
+
len = index+1 if node.last?
|
147
|
+
rec_len = prefix_recursive(node.mid, string, index+1)
|
148
|
+
end
|
149
|
+
len > rec_len ? len : rec_len
|
150
|
+
end
|
151
|
+
|
152
|
+
def push_recursive(node, string, index, value)
|
153
|
+
char = string[index]
|
154
|
+
node = Node.new(char, value) if node.nil?
|
155
|
+
if (char < node.char)
|
156
|
+
node.left = push_recursive(node.left, string, index, value)
|
157
|
+
elsif (char > node.char)
|
158
|
+
node.right = push_recursive(node.right, string, index, value)
|
159
|
+
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
160
|
+
node.mid = push_recursive(node.mid, string, index+1, value)
|
161
|
+
else
|
162
|
+
node.end = true
|
163
|
+
node.value = value
|
164
|
+
end
|
165
|
+
node
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns [char, value] if found
|
169
|
+
def get_recursive(node, string, index)
|
170
|
+
return nil if node.nil?
|
171
|
+
char = string[index]
|
172
|
+
if (char < node.char)
|
173
|
+
return get_recursive(node.left, string, index)
|
174
|
+
elsif (char > node.char)
|
175
|
+
return get_recursive(node.right, string, index)
|
176
|
+
elsif (index < string.length-1) # We're not at the end of the input string; add next char
|
177
|
+
return get_recursive(node.mid, string, index+1)
|
178
|
+
else
|
179
|
+
return node.last? ? [node.char, node.value] : nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|