Competitive Programming amp Problem Solving Tries Frank Maurix Contents What is a trie When to use tries Implementation and some operations Alternatives for implementation Compression ID: 485666
Download Presentation The PPT/PDF document "Honors Track:" is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Honors Track:Competitive Programming& Problem SolvingTries
Frank MaurixSlide2
ContentsWhat is a trie?When to use triesImplementation and some operationsAlternatives for implementationCompression
Suffix treeSlide3
What is a trie?Data StructureDigital tree, radix tree, prefix treeStores set of strings (dictionary)Characters as nodes
Position reflects prefix representedSlide4
When to use triesPros O(L) and O(L*A) operationsForm of radix sortx for all words with same prefix
S
uffix tree
Cons
O(L)
and
O(L*A)
operations
O(N*A) space complexityHorrible for floating point numbersNot a standard library
N = Number of nodes
L = Length of the word
A = Size of the alphabetSlide5
ImplementationKeep track of rootArray of childrenStore the number of childrenAlphabet = A, B,…, Z Uppercase only
Change character into value 0,…,25
int
c =
someChar
–
'A';
// -
'a'
for
lowercaseSlide6
Implementationimport
java.util
.*;
public class
ScaryProblem
{
TrieNode
root;
//Root of the
trie
void
solve()
{
root =
new
TrieNode
(
null
,
false
,
null
);
//here is the place where you should do some magic with tries
}
public static
void
main(
String[]
args
) {
new
ScaryProblem
().solve();
}
}
class
TrieNode
{
TrieNode
[]
children =
new
TrieNode
[26]
;
Character
ch
;
//last char of prefix, null for root
TrieNode
parent;
//pointer to parent
boolean
inDictionary
;
//Prefix
of this
node
in the
dictionary?
int
nOC
= 0;
//Number of children
TrieNode
(
Character
ch
,
boolean
used,
TrieNode
newParent
) {...}
}Slide7
OperationsSearchingInsertionWord deletionPrefix deletionRetrieving in sorted orderSlide8
Searchingchar[] word =
{'N', 'A', 'S', 'A'};
root.search
(word, 0
);
//Alternative: word as String and use
word.charAt
(index)
class
TrieNode
{
TrieNode
search(
char[]
word,
int
index)
{
//index should be 0 on initial call
if
(index ==
word.length
- 1)
{
//Node found or final node doesn’t exist
return
children[word[index] -
'A'];
}
else if
(children[word[index] -
'A']
==
null
)
{
//Node doesn't exist
return
null
;
}
else
{
//Keep searching
return
children[word[index] -
'A'].
search(word, index + 1);
}
}
}Slide9
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide10
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide11
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide12
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide13
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide14
InsertionInserting nodes may be necessary, but doesn't need to beExample 1: inserting “SPACES”Slide15
InsertionInserting nodes may be necessary, but doesn't need to beExample 2: inserting “NSA”Slide16
InsertionInserting nodes may be necessary, but doesn't need to beExample 2: inserting “NSA”Slide17
InsertionInserting nodes may be necessary, but doesn't need to beExample 2: inserting “NSA”Slide18
InsertionInserting nodes may be necessary, but doesn't need to beExample 2: inserting “NSA”Slide19
InsertionInserting nodes may be necessary, but doesn't need to beExample 2: inserting “NSA”Slide20
Insertionchar[] word = {'N', 'S', 'A'};
root.insert
(word, 0);
class
TrieNode
{
void
insert(
char[]
word,
int
index) {
//index 0 on initial call
if
(children[word[index]-'A'] ==
null
) {
//Next node doesn’t exist
nOC
++;
if
(index ==
word.length
- 1) {
children[word[index]-'A'] =
new
TrieNode
(word[index],
true
,
this
);
}
else
{
children[word[index]-'A'] =
new
TrieNode
(word[index],
false
,
this
);
children[word[index]-'A'].insert(word, index + 1);
}
}
else if
(index ==
word.length
- 1) {
children[word[index] - 'A'].
inDictionary
=
true
;
}
else
{
children[word[index] - 'A'].insert(word, index + 1);
}
}
}Slide21
DeletionSearch for corresponding nodeSet inDictionary for corresponding node to falseIf the node isn’t a leaf, you’re doneElse, one or more nodes can be removed
Removing the nodes
Don’t delete the root
If the current node is a leaf and not in the
dictionary
Remove the node
Recursive call to the parent and repeatSlide22
DeletionExample 1: deleting “SPACES”Slide23
DeletionExample 1: deleting “SPACES”Slide24
DeletionExample 1: deleting “SPACES”Slide25
DeletionExample 2: deleting “NSA”Slide26
DeletionExample 2: deleting “NSA”Slide27
DeletionExample 2: deleting “NSA”Slide28
DeletionExample 2: deleting “NSA”Slide29
DeletionExample 2: deleting “NSA”Slide30
Deletionvoid
removeWord
(
char[]
word) {
TrieNode
result =
root.search
(word, 0);
if
(result ==
null
)
{
//word not in
trie
}
else
if
(
result.nOC
== 0)
{
//node is a leaf
result.trieCleanup
();
}
else
{
//node isn’t a leaf
result.inDictionary
=
false
;
}
}
class
TrieNode
{
void
trieCleanup
()
{
//Delete current node &
check if parent should be deleted
if
(parent !=
null
)
{
//Never delete the root
parent.children
[
ch
- 'A'] =
null
;
parent.nOC
-
-;
if
(
parent.nOC
== 0 && !
parent.inDictionary
) {
parent.trieCleanup
();
}
}
}
}Slide31
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide32
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide33
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide34
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide35
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide36
Prefix deletionExample: deleting all words with prefix ‘SPA’Slide37
Prefix deletionvoid
removePrefix
(
char[]
word) {
TrieNode
result =
root.search
(word, 0);
if
(result
!=
null
)
{
result.trieCleanup
();
}
}Slide38
Retrieving in alphabetical orderPre-order tree traversalOnly report prefixes in dictionary
traverse(root);
void
traverse(
TrieNode
node) {
if
(
node.inDictionary
) {
report(node
);
//Or other fancy stuff
}
for
(
int
i
= 0;
i
< 26;
i
++) {
if
(
node.children
[
i
] !=
null
) {
traverse(
node.children
[
i
]);
}
}
}Slide39
Alphabetic successor of a nodeIf node isn’t a leafFind minimum on the subtree rooted at nodeStop as soon as you find a word in the dictionaryElseIf parent has a node with index higher than index of the current, then find minimum like before on that nodeElse repeat for parentSlide40
Alternatives for storing childrenA is the size of the alphabetL is the length of the wordN is the number of nodes
Operation
Array
HashMap
LinkedList
Insertion
O(A
.
L)
O(L)*
O(A
.
L
)
sorted
;
O(L)
unsorted
Deletion
O(L)
O(L)*
O(L)
(search
already
done
)
Search
O(L)
O(L)*
O(A
.
L)
Trie
traversal
O(N)**
O(N)**
O(N)**
sorted
,
O(A
.
log(A)
.
N)**
unsorted
* Expected O(L) time. Worst case O(A
.
L)
** Assuming report function takes O(1) timeSlide41
Array vs HashMap vs LinkedListArray
+ Simple
+ Small constants
+ Good for simple alphabet
- A pain with complex alphabet
- Always a lot of space
HashMap
+ Works with complex alphabet
+ Fast expected time
- Worst case still slow
- Worst case more space than array
- Constants worse than array
+ Low space usage
+ Works with complex alphabet
+ Small constants
- Very slow
LinkedListSlide42
CompressionMerge nodesAdapt operations appropriatelySlide43
CompressionAfter deletion, more compression may be possibleInsertion after compression:Possibly split a node, insert and compressSlide44
Suffix treeTake all suffixes of a wordInsert all into a trieOffers many fast string operationsWorst case O(L2
) nodes
BANANA
ANANA
NANA
ANA
NA
A
All suffixes of
BANANASlide45
Applications of suffix treesNumber of occurrences of a pattern in a textSearch for the pattern, only consider that subtreeResult = number of nodes in that subtree with inDictionary = trueLongest Common Substring of two strings
Insert both strings into a suffix tree
For each node, store which strings represent them
Find deepest node represented by both stringsSlide46
Questions