Anagrams and Hash Tables Olac Fuentes University of
Anagrams and Hash Tables Olac Fuentes University of Texas at El Paso
Can we find anagrams in constant time? Yes, with time O(v) to preprocess the words, where v is the size of the vocabulary. We will store the words in the English dictionary in a way that allows to find all the anagrams of any given word in constant time. Let sort(w), where w is a string, be the string that contains the same characters as w (including repetitions) sorted in ascending order. So sort(“java”) = “aajv”. Observation: words w and x are anagrams of each other iff sort(w) == sort(x) For example sort(“aunt”) = “anut”; sort(“tuna”) = “anut”, so “aunt” and “tuna” are anagrams. We will use a hash table, using the words with sorted characters as keys. The node that contains the key S also contains a reference to a list of words {W 1, . . . Wn} such that sort(W 1)=sort(W 2)=. . . =S, thus all words in the list are anagrams of one another. Let’s see how this works with an example.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo edit / code coed / tide tied /
CLASS DEFINITIONS public class hash. Table. Chaining{ private Sorted. String. Node [] H; public hash. Table. Chaining(int n){ } H = new Sorted. String. Node[n]; for(int i=0; i<n; i++) H[i] = null; } public class Sorted. String. Node{ public String Sorted. String; public Sorted. String. Node next; public String. Node anagrams; } public Sorted. String. Node(String S, Sorted. String. Node n){ Sorted. String = S; next = n; anagrams = null; } public class String. Node{ public String word; public String. Node next; } public String. Node(String S, String. Node n){ word = S; next = n; }
First we need to define a hash function that will allow to distribute the words more or less uniformly in the table. Here are examples of bad hash functions: h(S) = S. length()%H. length //All words of the same length hash to the same location h(S) = ((int) S. char. At(0))%H. length //All words that start with the same letter hash to the same location A better way is to view a string as a number in a base-26 number system (since we have 26 letters), with a=0, b=1, and so on For example, in a base 10 system value(2017) = 7*100 + 1*101 + 0*102 + 2*103 Similarly, value(“be”) = 4*260 + 1*261 Thus a good hash function would be: h(w) = value(w)%H. length; However, for our application, we need all words that are anagrams of one another to hash to the same location, so we will use the hash function h(w) = value(sort(w))%H. length;
Consider the following vocabulary: EXAMPLE back code coed diet edit flow lake tide tied We will build a hash table of size 5 (in practice you should use much larger sizes) to store the words, organized as groups of anagrams.
Initially: H: 0 / 1 / 2 / 3 / 4 /
Initially: H: 0 / 1 / 2 / 3 / 4 / Insert “back”. Sorted(“back”) = “abck” and h(sorted(“back”)) = h(“abck”) = 3 Since “abck” is not in the hash table, we create a new node containing the key “abck” and insert the original word “back” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 / back / / Insert “back”. Sorted(“back”) = “abck” and h(sorted(“back”)) = h(“abck”) = 3 Since “abck” is not in the hash table, we create a new node containing the key “abck” and insert the original word “back” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 / back / / Insert “code”. Sorted(“code”) = “cdeo” and h(sorted(“code”)) = h(“cdeo”) = 3 Since “cdeo” is not in the hash table, we create a new node containing the key “cdeo” and insert the original word “code” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 back / cdeo / code / / Insert “code”. Sorted(“code”) = “cdeo” and h(sorted(“code”)) = h(“cdeo”) = 3 Since “cdeo” is not in the hash table, we create a new node containing the key “cdeo” and insert the original word “code” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 back / cdeo / code / / Insert “coed”. Sorted(“coed”) = “cdeo” and h(sorted(“coed”)) = h(“cdeo”) = 3 The sorted word “cdeo” is already in the hash table, so we insert “coed” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 back / cdeo / code coed / / Insert “coed”. Sorted(“coed”) = “cdeo” and h(sorted(“coed”)) = h(“cdeo”) = 3 The sorted word “cdeo” is already in the hash table, so we insert “coed” into the list associated with that node.
H: 0 / 1 / 2 / abck 3 4 back / cdeo / code coed / / Insert “diet”. Sorted(“diet”) = “deit” and h(sorted(“diet”)) = h(“deit”) = 4 Since “deit” is not in the hash table, we create a new node containing the key “deit” and insert the original word “diet” into the list associated with that node.
H: 0 / 1 / 2 / 3 abck 4 deit / back / diet / cdeo / code coed / Insert “diet”. Sorted(“diet”) = “deit” and h(sorted(“diet”)) = h(“deit”) = 4 Since “deit” is not in the hash table, we create a new node containing the key “deit” and insert the original word “diet” into the list associated with that node.
H: 0 / 1 / 2 / 3 abck 4 deit / back / diet / cdeo / code coed / Insert “edit”. Sorted(“edit”) = “deit” and h(sorted(“diet”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “edit” into the list associated with that node.
H: 0 / 1 / 2 / 3 abck 4 deit back / diet / cdeo / edit / code coed / Insert “edit”. Sorted(“edit”) = “deit” and h(sorted(“diet”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “edit” into the list associated with that node.
H: 0 / 1 / 2 / 3 abck 4 deit back / diet / cdeo / edit / code coed / Insert “flow”. Sorted(“flow”) = “flow” and h(sorted(“flow”)) = h(“flow”) = 2 Since “flow” is not in the hash table, we create a new node containing the key “flow” and insert the original word “flow” into the list associated with that node.
H: 0 / 1 / 2 flow 3 abck 4 deit / flow back / diet / / cdeo / edit / code coed / Insert “flow”. Sorted(“flow”) = “flow” and h(sorted(“flow”)) = h(“flow”) = 2 Since “flow” is not in the hash table, we create a new node containing the key “flow” and insert the original word “flow” into the list associated with that node.
H: 0 / 1 / 2 flow 3 abck 4 deit / flow back / diet / / cdeo / edit / code coed / Insert “lake”. Sorted(“lake”) = “aekl” and h(sorted(“lake”)) = h(“aekl”) = 0 Since “aekl” is not in the hash table, we create a new node containing the key “aekl” and insert the original word “lake” into the list associated with that node.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo / edit / code coed / Insert “lake”. Sorted(“lake”) = “aekl” and h(sorted(“lake”)) = h(“aekl”) = 0 Since “aekl” is not in the hash table, we create a new node containing the key “aekl” and insert the original word “lake” into the list associated with that node.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo / edit / code coed / Insert “tide”. Sorted(“tied”) = “deit” and h(sorted(“tied”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “tide” into the list associated with that node.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo edit / coed code tide / / Insert “tide”. Sorted(“tied”) = “deit” and h(sorted(“tied”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “tide” into the list associated with that node.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo edit / coed code tide / / Insert “tied”. Sorted(“tied”) = “deit” and h(sorted(“tied”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “tied” into the list associated with that node.
H: aekl / 2 flow / 3 abck 4 deit 0 1 lake / flow / / back / diet / cdeo edit / code coed / tide tied / Insert “tied”. Sorted(“tied”) = “deit” and h(sorted(“tied”)) = h(“deit”) = 4 The sorted word “deit” is already in the hash table, so we insert “tied” into the list associated with that node.
CLASS DEFINITIONS public class hash. Table. Chaining{ private Sorted. String. Node [] H; public hash. Table. Chaining(int n){ } H = new Sorted. String. Node[n]; for(int i=0; i<n; i++) H[i] = null; } public class Sorted. String. Node{ public String Sorted. String; public Sorted. String. Node next; public String. Node anagrams; } public Sorted. String. Node(String S, Sorted. String. Node n){ Sorted. String = S; next = n; anagrams = null; } public class String. Node{ public String word; public String. Node next; } public String. Node(String S, String. Node n){ word = S; next = n; }
WHAT IF WE USE LINEAR PROBING? CLASS DEFINITIONS public class hash. Table. Chaining{ private Sorted. String. Node [] H; public hash. Table. Chaining(int n){ } H = new Sorted. String. Node[n]; for(int i=0; i<n; i++) H[i] = null; } public class Sorted. String. Node{ public String Sorted. String; public String. Node anagrams; } public Sorted. String. Node(String S, Sorted. String. Node n){ Sorted. String = S; anagrams = null; } public class String. Node{ public String word; public String. Node next; } public String. Node(String S, String. Node n){ word = S; next = n; }
H: 0 / 1 / 2 / abck 3 4 back / / Insert “code” into table h(“cdeo”) = 3, but slot 3 is already taken. Easy, insert “cdeo” into next available slot.
H: 0 / 1 / 2 / 3 abck 4 cdeo back / code / Insert “code” into table h(“cdeo”) = 3, but slot 3 is already taken. Easy, insert “cdeo” into next available slot.
H: 0 / 1 / 2 / 3 abck 4 cdeo back code / coed / Insert “coed” into table h(“cdeo”) = 3, but slot 3 is already taken, but we find “cdeo” in the next slot. We now insert “coed” into the appropriate list as before.
- Slides: 30