ruby-anagrams 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/anagrams/anagrams.rb +91 -0
- data/lib/anagrams/enumerable.rb +25 -0
- data/lib/anagrams/node.rb +71 -0
- data/lib/anagrams/root.rb +44 -0
- data/lib/anagrams/subtrees.rb +47 -0
- data/lib/ruby-anagrams.rb +5 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b4354f4fdcda04c67e8f3b2e11afbe9b6999fc22
|
4
|
+
data.tar.gz: 7cf6680ff7f4de0aa5aff21768852fab28e51d7a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 28216d0fefce0b01b630e56e7eec4d9cf330183c7e76666cd94db7bd17fd8bd8dc5b7a14d7c6121069ec35a70bfce2952ca5aac6780f20b27582e192339b3ada
|
7
|
+
data.tar.gz: dceb898b96b5f0b67cdbd014fb1d8bde14586725ed18c35f4f5d6128037c725cf3c3f027067def25a37502b4e26f5be3c131cdbf588c62cf110c0ccb549b01b9
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module RubyAnagrams
|
2
|
+
# Provides anagram solver behavior to the trie data structure.
|
3
|
+
# @author Connor Lay
|
4
|
+
module Anagrams
|
5
|
+
require 'prime'
|
6
|
+
|
7
|
+
# A Hash associating symbols with unique prime numbers.
|
8
|
+
SYM_PRIMES = (:a..:z).to_a.zip(Prime.first(26)).to_h
|
9
|
+
|
10
|
+
# Returns all words in the trie data structure that are anagrams
|
11
|
+
# of the string provided. "*" indicates a wildcard.
|
12
|
+
# @note Default behavior finds complete anagrams that use every character
|
13
|
+
# in the string. Partial anagrams can be included by setting include_partial
|
14
|
+
# to true.
|
15
|
+
# @example without partial anagrams
|
16
|
+
# root << "rise"
|
17
|
+
# root << "sire"
|
18
|
+
# root << "rie"
|
19
|
+
# #anagrams "rise" #=> ['rise', 'sire']
|
20
|
+
# @example with partial anagrams
|
21
|
+
# root << "rise"
|
22
|
+
# root << "sire"
|
23
|
+
# root << "rie"
|
24
|
+
# #anagrams "rise", include_partial: true #=> ['rie', 'rise', 'sire']
|
25
|
+
# @example with wildcards
|
26
|
+
# root << "bin"
|
27
|
+
# root << "ban"
|
28
|
+
# root << "bun"
|
29
|
+
# #anagrams "b*n" #=> ['ban', 'bin', 'bun']
|
30
|
+
# @param string [String] the string to find anagrams of.
|
31
|
+
# @param include_partial [Boolean] include partial anagrams?
|
32
|
+
# @return [Array<String>] all anagrams of the given string.
|
33
|
+
def anagrams string, include_partial: false
|
34
|
+
symbols = str_to_sym_a string
|
35
|
+
anagrams = []
|
36
|
+
find_symbol_permutations(symbols).each do |permutation|
|
37
|
+
anagrams.concat find_anagrams(as_product(permutation), include_partial: include_partial)
|
38
|
+
end
|
39
|
+
anagrams.uniq.sort
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
# Returns anagrams in the trie data structure by performing depth-first search,
|
44
|
+
# following subtrees whose symbol's associated prime number is a prime factor
|
45
|
+
# of the product representing a string. The words of visited terminal nodes
|
46
|
+
# are returned as an array.
|
47
|
+
# @note Default behavior finds words that consume all prime factors
|
48
|
+
# of the given product. Words that do not consume all prime factors can
|
49
|
+
# be included by setting include_partial to true.
|
50
|
+
# @param product [Integer] the product representing the string to find
|
51
|
+
# anagrams of.
|
52
|
+
# @param include_partial [Boolean] include partial anagrams?
|
53
|
+
# @return [Array<String>] all anagrams whose product divides into the given
|
54
|
+
# product.
|
55
|
+
def find_anagrams product, include_partial: false
|
56
|
+
anagrams = []
|
57
|
+
anagrams << word if terminal? && (include_partial ? true : product == 1)
|
58
|
+
@children.each do |symbol,child|
|
59
|
+
if product % SYM_PRIMES[symbol] == 0
|
60
|
+
anagrams += child.find_anagrams(product / SYM_PRIMES[symbol], include_partial: include_partial)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
anagrams
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns all permutations of the given set of symbols, where "*" wildcards
|
67
|
+
# are replaced with all possible permutations of symbols in the trie's
|
68
|
+
# alphabet.
|
69
|
+
# @example with 1 wildcard
|
70
|
+
# alphabet = [:a, :b, :c]
|
71
|
+
# #find_symbol_permutations [:a, :*, :c] #=> [[:a, :a, :c], [:a, :b, :c], [:a, :c, :c]]
|
72
|
+
# @param symbols [Array<Symbol>] the symbols to permute.
|
73
|
+
# @return [Array<Array<Symbol>>] all permutations of the symbols.
|
74
|
+
def find_symbol_permutations symbols
|
75
|
+
symbol_permutations = []
|
76
|
+
non_wild_symbols = symbols.reject{|s| s == :*}
|
77
|
+
(:a..:z).to_a.repeated_permutation(symbols.count :*).each do |permutation|
|
78
|
+
symbol_permutations << non_wild_symbols + permutation
|
79
|
+
end
|
80
|
+
symbol_permutations.empty? ? [symbols] : symbol_permutations
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the product of each symbol's associated prime number.
|
84
|
+
# @param symbols [Array<Symbol>] the symbols to multiply.
|
85
|
+
# @return [Integer] the product of each symbol's associated prime number.
|
86
|
+
def as_product symbols
|
87
|
+
symbols.inject(1) { |acc,sym| acc * SYM_PRIMES[sym] }
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RubyAnagrams
|
2
|
+
# Provides enumerable behavior to the trie data structure.
|
3
|
+
# @author Connor Lay
|
4
|
+
module Enumerable
|
5
|
+
|
6
|
+
include ::Enumerable
|
7
|
+
|
8
|
+
# Calls a block for each word in the trie data strucutre. If no block is given,
|
9
|
+
# an Enumerator is returned.
|
10
|
+
# @return [Enumerator] the enumerator for the words in the trie data structure.
|
11
|
+
def each &block
|
12
|
+
enumerator = Enumerator.new do |y|
|
13
|
+
y << word if terminal?
|
14
|
+
@children.each_value do |child|
|
15
|
+
child.each { |word| y << word }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
block.nil? ? enumerator : enumerator.each(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :size :count
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module RubyAnagrams
|
2
|
+
# A representation of a Node in the trie data structure.
|
3
|
+
# @author Connor Lay
|
4
|
+
class Node
|
5
|
+
|
6
|
+
include Subtrees
|
7
|
+
include Enumerable
|
8
|
+
include Anagrams
|
9
|
+
|
10
|
+
attr_reader :symbol, :children, :parent
|
11
|
+
|
12
|
+
# Creates a new, non-terminal node with no children.
|
13
|
+
# @param symbol [Symbol, nil] the symbol the node represents.
|
14
|
+
# @param parent [Node, nil] the parent of the node.
|
15
|
+
# @return [Node] the node just created.
|
16
|
+
def initialize symbol = nil, parent = nil
|
17
|
+
@symbol = symbol
|
18
|
+
@parent = parent
|
19
|
+
@children = {}
|
20
|
+
@terminal = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds a new child node representing a symbol to this node's children.
|
24
|
+
# @param symbol [Symbol] the symbol the node represents.
|
25
|
+
# @param child [Node] the node to add as a child.
|
26
|
+
# @return [Node] the node just added.
|
27
|
+
def []= symbol, child
|
28
|
+
@children[symbol] = child
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the child node associated with a symbol.
|
32
|
+
# @param symbol [Symbol]
|
33
|
+
# @return [Node] the child node associated with the symbol.
|
34
|
+
def [] symbol
|
35
|
+
@children[symbol]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the node as a terminal.
|
39
|
+
def terminal!
|
40
|
+
@terminal = true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Is the node a terminal?
|
44
|
+
# @return [Boolean] true if the node is a terminal, false otherwise
|
45
|
+
def terminal?
|
46
|
+
@terminal
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the word the node represents based on its symbol and
|
50
|
+
# the symbols of its parents.
|
51
|
+
# @example called on the node representing :e
|
52
|
+
# :a
|
53
|
+
# :p
|
54
|
+
# :p
|
55
|
+
# :l
|
56
|
+
# :e
|
57
|
+
# #word #=> "apple"
|
58
|
+
# @return [String] the word the node represents.
|
59
|
+
def word
|
60
|
+
@parent ? "#{@parent.word}#{@symbol}" : "#{@symbol}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"<#{self.class}:#{self.object_id}, "\
|
65
|
+
"@symbol=#{@symbol ? ":#{@symbol}" : "nil"}, "\
|
66
|
+
"@word=#{terminal? ? word : "nil"}, "\
|
67
|
+
"@children=#{@children.each_key.to_a}>"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Namespace for the Ruby-Anagrams gem.
|
2
|
+
module RubyAnagrams
|
3
|
+
# A representation of the Root node of the trie data structure.
|
4
|
+
# @author Connor Lay
|
5
|
+
class Root < Node
|
6
|
+
# Creates a new trie.
|
7
|
+
# @param path [String, nil] the path to a dictionary text file.
|
8
|
+
# @return [Root] the Root node of the trie just created.
|
9
|
+
def initialize path = nil
|
10
|
+
super()
|
11
|
+
if path
|
12
|
+
file = File.open path, "r"
|
13
|
+
file.each_line { |line| self << line.downcase.strip }
|
14
|
+
file.close
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds a word to the trie.
|
19
|
+
# @param word [String] the new word to add to the trie.
|
20
|
+
# @return [String] the word just added to the trie.
|
21
|
+
def << word
|
22
|
+
symbols = str_to_sym_a word
|
23
|
+
add_to_subtree symbols
|
24
|
+
end
|
25
|
+
|
26
|
+
# If the trie contains the word.
|
27
|
+
# @param word [String] the word to search for.
|
28
|
+
# @return [Boolean] true if the word is found, false otherwise.
|
29
|
+
def include? word
|
30
|
+
symbols = str_to_sym_a word
|
31
|
+
search_subtree symbols
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a symbol array based on the chatacters of a string.
|
35
|
+
# @example
|
36
|
+
# str_to_sym_a "apple" #=> [:a, :p, :p, :l, :e]
|
37
|
+
# @param string [String] the string to process
|
38
|
+
# @return [Array<Symbol>] an array of symbols
|
39
|
+
def str_to_sym_a string
|
40
|
+
string.chars.map { |char| char.to_sym }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RubyAnagrams
|
2
|
+
# Provides subtree branching and searching behavior to the trie data strucutre.
|
3
|
+
# @author Connor Lay
|
4
|
+
module Subtrees
|
5
|
+
|
6
|
+
# Returns the number of nodes in the subtree. When called on Root, returns
|
7
|
+
# the number of nodes in the entire trie data structure.
|
8
|
+
# @return [Integer] the number of nodes in the subtree.
|
9
|
+
def node_count
|
10
|
+
nodes = 1
|
11
|
+
@children.each_value do |child|
|
12
|
+
nodes += child.node_count
|
13
|
+
end
|
14
|
+
nodes
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
# Descends the trie data structure, adding nodes when needed, for a given
|
19
|
+
# sequence of symbols. The last node visited is designated as a terminal.
|
20
|
+
# @param symbols [Array<Symbol>] the symbol sequence to follow.
|
21
|
+
# @return [String] the word represented by the last node visited.
|
22
|
+
def add_to_subtree symbols
|
23
|
+
if symbols.empty?
|
24
|
+
terminal!
|
25
|
+
return word
|
26
|
+
end
|
27
|
+
s = symbols.slice! 0
|
28
|
+
unless child = @children[s]
|
29
|
+
child = Node.new s, self
|
30
|
+
@children[s] = child
|
31
|
+
end
|
32
|
+
child.add_to_subtree symbols
|
33
|
+
end
|
34
|
+
|
35
|
+
# Performs depth-first search, following a sequence of symbols, on the trie
|
36
|
+
# data structure. Returns true if the last node visited is a terminal.
|
37
|
+
# @param symbols [Array<Symbol>] the symbol sequence to follow.
|
38
|
+
# @return [Boolean] true if the last node visited is a terminal, false otherwise.
|
39
|
+
def search_subtree symbols
|
40
|
+
return true if symbols.empty? && terminal?
|
41
|
+
s = symbols.slice! 0
|
42
|
+
return false unless child = @children[s]
|
43
|
+
child.search_subtree symbols
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-anagrams
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Connor Lay
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-its
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A trie-based anagram solver.
|
70
|
+
email: connor.lay@me.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- lib/anagrams/anagrams.rb
|
76
|
+
- lib/anagrams/enumerable.rb
|
77
|
+
- lib/anagrams/node.rb
|
78
|
+
- lib/anagrams/root.rb
|
79
|
+
- lib/anagrams/subtrees.rb
|
80
|
+
- lib/ruby-anagrams.rb
|
81
|
+
homepage: https://github.com/connorlay/ruby-anagrams
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.4.6
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Ruby Anagrams
|
105
|
+
test_files: []
|
106
|
+
has_rdoc:
|