Unit Iii
Unit Iii
SYLLABUS:-
Tree Algorithms: Fenwick Tree, Segment Tree – Applications-Range Sum Queries. Treap-
Applications- Kth Largest Element in an Array. Page No: (01-34)
Trie: Introduction, Suffix Tree, Applications-Index Pairs of a String, Longest word with all
prefixes, top K frequent words. Page No: (35-72)
Tree Algorithms:
Applications:
1. Fenwick Tree.
2. Segment Tree – Applications-Range Sum Queries.
3. Treap- Applications- Kth Largest Element in an Array.
1. Fenwick Tree- also known as Binary Indexed Tree (BIT)—were invented by Peter
[Link] in 1994. Fenwick Tree provides a way to represent an array of numbers in an
array, allowing prefix sums to be calculated efficiently. For example, an array is [2, 3, -1, 0, 6]
the length 3 prefix [2, 3, -1] with sum 2 + 3 + -1 = 4). Calculating prefix sums efficiently is
useful in various scenarios. Let's start with a simple problem.
We are given an array a[], and we want to be able to perform two types of operations on it.
1. Change the value stored at an index i. (This is called a point update operation)
2. Find the sum of a prefix of length k. (This is called a range sum query)
This is a perfect solution, but unfortunately the time required to calculate a prefix sum is
proportional to the length of the array, so this will usually time out when large number of such
intermingled operations are performed.
Can we do better than this? Off course. One efficient solution is to use segment tree that can
perform both operation in O(logN) time. Using binary Indexed tree also, we can perform both the
tasks in O(logN) time. But then why learn another data structure when segment tree can do the
work for us. It’s because binary indexed trees require less space and are very easy to
implement during programming contests.
Before starting with binary indexed tree, we need to understand a particular bit manipulation trick.
Here it goes.
How to isolate?
Example:-
The last set bit is given by x&(-x) = (10)1(0) & (01)1(0) = 0010 = 2(in decimal)
We know the fact that each integer can be represented as sum of powers of two. Similarly, for a
given array of size N, we can maintain an array BIT[] such that, at any index we can store sum of
some numbers of the given array. This can also be called a partial sum tree.
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
The above picture shows the binary indexed tree, each enclosed box of which denotes the value
BIT[index] and each BIT[index] stores partial sum of some numbers.
Notice
To generalize this every index i in the BIT[] array stores the cumulative sum from the
index i to i - (1<<r) + 1 (both inclusive), where r represents the last set bit in the index i
Applications:
Fenwick tree can be used to calculate Range Sum, i.e finding the sum within range. Sum(1,7) in
the below example array is
Value 5 2 9 -3 5 20 10 -7 2 3 -4 0 -2 15 5
Index 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
int sum(int i)
{
int sum = 0;
while ( i > 0 )
{
sum +=BIT[i];
i -= i & -i;//flip the last set bit
}
return sum;
}
Sum(7) = sum(00111)
= 10 + 25 + 12 =48
To compute the sum (7) while loop iterates only three times
= BIT [01000]
= BIT [8]
To compute sum(8) while loop iterates only [Link] is faster to compute the range sums in large
arrays.
Updation:
When we Update the array recomputing the fenwick tree will not be too costly, see the below
code for updation.
T[i] += k;
i += i & -i; // add last set bit
}
}
Example :- add(4,10) i.e making index 4 value to be 7
BIT[00100] = 13 + 10 =23
BIT[01000] = 41 + 10 = 51
import [Link].*;
class FenWickTree
{
int[] nums;
int[] BIT;
int n;
public FenWickTree(int[] nums)
{
[Link] = nums;
n = [Link];
BIT = new int[n + 1];
for (int i = 0; i < n; i++)
{
init(i, nums[i]);
}
}
public void init(int i, int val) {
i++;
while (i <= n) {
BIT[i] += val;
i += (i & -i);
}
}
void update(int i, int val) {
int diff = val - nums[i];
nums[i] = val;
init(i, diff);
}
}
return sum;
}
{
int ind = [Link]();
int val= [Link]();
[Link](ind,val);
}
}
}
}
Sample input :-
85
1 2 13 4 25 16 17 8
126
107
2 2 18
2 4 17
127
Out put :
75
86
80
2. Segment Tree:-
Segment tree or Segtree is basically a binary tree used for storing the intervals or
segments.
Each node in the segment tree represents an interval.
Consider an array A of size N and a corresponding Segtree T:
The root of T will represent the whole array A[0:N-1].
Each leaf in the segtree T will represent a single element A[i] such that 0 <= i < N.
The internal nodes in the segtree T represent union of elementary intervals A[i:j] where 0
<= i < j < N.
The root of the segtree will represent the whole array A[0:N-1]. Then we will break the
interval or segment into half and the two children of the root will represent the A[0:(N-1) /
2] and A[(N-1) / 2 + 1:(N-1)].
So in each step we will divide the interval into half and the two children will represent the
two halves, so the height of the segment tree will be log2N. There are N leaves
representing the N elements of the array. The number of internal nodes is N-1. So total
number of nodes are 2*N - 1.
Once we have built a segtree we cannot change its structure i.e., its structure is static. We
can update the values of nodes but we cannot change its structure. Segment tree is
recursive in nature. Because of its recursive nature, Segment tree is very easy to
implement. Segment tree provides two operations:
Update: In this operation we can update an element in the Array and reflect the
corresponding change in the Segment tree.
Query: In this operation we can query on an interval or segment and return the answer to
the problem on that particular interval.
The maximum number of nodes in a Segment Tree depends on the size of the input array.
Explanation:
Formula:
Max nodes=2×2⌈log2(n)⌉ - 1.
Example Calculations:
For n=6
log2(6)=3
Maximum nodes = 2×23−1=15
For n=8
log2(8)=3
Maximum nodes = = 2×23−1=15
For n=10
log2(10)=4
Maximum nodes = 2×24−1=31.
Note: The number of nodes in a Segment Tree is at most O(4n) in the worst case.
Implementation:
Since a segment tree is a binary tree, we can use a simple linear array to represent the
segment tree. In almost any segment tree problem we need to think about what we need to store in
the segment tree?
For example, if we want to find the sum of all the elements in an array from index left to right,
then at each node (except leaf nodes) we will store the sum of its children nodes. If we want to
find the minimum of all the elements in an array from index left to right, then at each node (except
leaf nodes) we will store the minimum of its children nodes.
Once we know what we need to store in the segment tree we can build the tree
using recursion (bottom-up approach). We will start with the leaves and go up to the root and
update the corresponding changes in the nodes that are in the path from leaves to root. Leaves
represent a single element. In each step we will merge two children to form an internal node. Each
internal node will represent a union of its children’s intervals. Merging may be different for
different problems. So, recursion will end up at root which will represent the whole array.
For update, we simply have to search the leaf that contains the element to update. This can
be done by going to either on the left child or the right child depending on the interval which
contains the element. Once we found the leaf, we will update it and again use the bottom-up
approach to update the corresponding change in the path from leaf to root.
To make a query on the segment tree we will be given a range from l to r. We will recurse
on the tree starting from the root and check if the interval represented by the node is completely in
the range from l to r. If the interval represented by a node is completely in the range from l to r,
we will return that node’s value.
Let us see how to use segment tree and what we will store in the segment tree in this
problem. As we know that each node of the segtree will represent an interval or segment. In this
problem we need to find the sum of all the elements in the given range. So in each node we will
store the sum of all the elements of the interval represented by the node. How do we do that? We
10 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
will build a segment tree using recursion (bottom-up approach) as explained above. Each leaf will
have a single element. All the internal nodes will have the sum of both of its children.
To update an element we need to look at the interval in which the element is and recurse
accordingly on the left or the right child.
void update(int node, int start, int end, int idx, int val)
{
if(start == end)
{
// Leaf node
11 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
A[idx] += val;
tree[node] += val;
}
else
{
int mid = (start + end) / 2;
if(start <= idx and idx <= mid)
{
// If idx is in the left child, recurse on the left child
update(2*node+1, start, mid, idx, val);
}
else
{
// if idx is in the right child, recurse on the right child
update(2*node+2, mid+1, end, idx, val);
}
// Internal node will have the sum of both of its children
tree[node] = tree[2*node] + tree[2*node+1];
}
}
Complexity of update will be O(logN).
If the range represented by a node is completely outside the given range, we will simply
return 0. If the range represented by a node is completely inside the given range, we will return
the value of the node which is the sum of all the elements in the range represented by the node.
And if the range represented by a node is partially inside and partially outside the given range, we
will return sum of the left child and the right child. Complexity of query will be O(logN).
12 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Sometimes problems will ask you to update an interval from l to r, instead of a single
element. One solution is to update all the elements one by one. Complexity of this approach will
be O(N) per operation since where are N elements in the array and updating a single element will
take O(logN) time.
To avoid multiple call to update function, we can modify the update function to work on
an interval.
void updateRange(int node, int start, int end, int l, int r, int val)
{
// out of range
if (start > end or start > r or end < l)
return;
// Current node is a leaf node
if (start == end)
{
// Add the difference to current node
tree[node] += val;
return;
}
// If not a leaf node, recur for children.
int mid = (start + end) / 2;
updateRange(node*2+1, start, mid, l, r, val);
updateRange(node*2 + 2, mid + 1, end, l, r, val);
// Use the result of children calls to update this node
tree[node] = tree[node*2+1] + tree[node*2+2];
}
Let's be Lazy i.e., do work only when needed. How ? When we need to update an interval,
we will update a node and mark its child that it needs to be updated and update it when needed.
For this we need an array lazy[] of the same size as that of segment tree. Initially all the elements
of the lazy[] array will be 0 representing that there is no pending update. If there is non-zero
element lazy[k] then this element needs to update node k in the segment tree before making any
query operation.
1. If current segment tree node has any pending update, then first add that pending update to
current node.
2. If the interval represented by current node lies completely in the interval to update, then
update the current node and update the lazy[] array for children nodes.
3. If the interval represented by current node overlaps with the interval to update, then update the
nodes as the earlier update function
13 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Since we have changed the update function to postpone the update operation, we will have
to change the query function also. The only change we need to make is to check if there is any
pending update operation on that node. If there is a pending update operation, first update the
node and then work same as the earlier query function.
void updateRange (int node, int start, int end, int l, int r, int val)
{
if(lazy[node] != 0)
{
// This node needs to be updated
tree[node] += (end - start + 1) * lazy[node]; // Update it
if(start != end)
{
lazy[node*2+1] += lazy[node]; // Mark child as lazy
lazy[node*2+2] += lazy[node]; // Mark child as lazy
}
lazy[node] = 0; // Reset it
}
if(start > end or start > r or end < l) // Current segment is not within range [l, r]
return;
if(start >= l and end <= r)
{
// Segment is fully within range
tree[node] += (end - start + 1) * val;
if(start != end)
{
// Not leaf node
lazy[node*2+1] += val;
lazy[node*2+2] += val;
}
return;
}
int mid = (start + end) / 2;
updateRange(node*2+1, start, mid, l, r, val); // Updating left child
updateRange(node*2 +2, mid + 1, end, l, r, val); // Updating right child
tree[node] = tree[node*2+1] + tree[node*2+2]; // Updating root with max value
}
14 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
}
lazy[node] = 0; // Reset it
}
if(start >= l and end <= r) // Current segment is totally within range [l, r]
return tree[node];
int mid = (start + end) / 2;
int p1 = queryRange(node*2+1, start, mid, l, r); // Query left child
int p2 = queryRange(node*2 +2, mid + 1, end, l, r); // Query right child
return (p1 + p2);
}
Example-1:
Java program for segment tree (get, update get range sum example):
[Link]
import [Link].*;
class SegmentTree
{
private int[] tree;
private int[] nums;
private int n;
15 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
}
int mid = left + (right - left) / 2;
buildTree(2 * treeIndex + 1, left, mid); // Build left subtree
buildTree(2 * treeIndex + 2, mid + 1, right); // Build right subtree
tree[treeIndex] = tree[2 * treeIndex + 1] + tree[2 * treeIndex + 2]; // Merge results
}
private int queryRange(int treeIndex, int left, int right, int queryLeft, int queryRight)
{
// If the current segment is completely outside the query range
if (right < queryLeft || left > queryRight)
{
return 0;
}
// If the current segment is completely inside the query range
if (left >= queryLeft && right <= queryRight)
{
return tree[treeIndex];
}
// If the current segment overlaps with the query range
int mid = left + (right - left) / 2;
int leftSum = queryRange(2 * treeIndex + 1, left, mid, queryLeft, queryRight);
int rightSum = queryRange(2 * treeIndex + 2, mid + 1, right, queryLeft, queryRight);
return leftSum + rightSum;
}
private void updateTree(int treeIndex, int left, int right, int index, int diff)
{
// If the index is outside the current segment
16 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
17 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
}
}
Sample input :-
85
4 2 13 4 25 16 17 8
126
107
2 2 18
2 4 17
127
Output :-
75
89
80
Example-2:
import [Link].*;
class SegmentTreeMax
{
private int[] tree;
private int[] nums;
private int n;
18 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
private int queryRangeMax(int treeIndex, int left, int right, int queryLeft, int queryRight) {
// If the current segment is completely outside the query range
if (right < queryLeft || left > queryRight) {
return Integer.MIN_VALUE; // Return minimum value to avoid affecting the result
}
// If the current segment is completely inside the query range
if (left >= queryLeft && right <= queryRight) {
return tree[treeIndex];
}
// If the current segment overlaps with the query range
int mid = left + (right - left) / 2;
int leftMax = queryRangeMax(2 * treeIndex + 1, left, mid, queryLeft, queryRight);
int rightMax = queryRangeMax(2 * treeIndex + 2, mid + 1, right, queryLeft, queryRight);
return [Link](leftMax, rightMax);
}
19 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
private void updateTree(int treeIndex, int left, int right, int index, int newValue) {
// If the index is outside the current segment
if (index < left || index > right) {
return;
}
// If the current segment is a leaf node
if (left == right) {
tree[treeIndex] = newValue;
return;
}
// If the index is within the current segment
int mid = left + (right - left) / 2;
updateTree(2 * treeIndex + 1, left, mid, index, newValue); // Update left subtree
updateTree(2 * treeIndex + 2, mid + 1, right, index, newValue); // Update right subtree
// Merge results: update the current node with the maximum of left and right subtrees
tree[treeIndex] = [Link](tree[2 * treeIndex + 1], tree[2 * treeIndex + 2]);
}
20 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
}
}
3. Treap:
A treap is a data structure which combines binary tree and binary heap (hence the name: tree +
heap ⇒ Treap).
Treap is a Balanced Binary Search Tree, but not guaranteed to have height as O(log n).
The idea is to use Randomization and Binary Heap property to maintain balance with high
probability.
The expected time complexity of search, insert and delete is O(log n).
More specifically, treap is a data structure that stores pairs(X,Y) in a binary tree in such a way
that it is abinary search tree by X and a binary heap by Y.
If some node of the tree contains values (X0,Y0), all nodes in the left subtree have X<=X0 all
nodes in the right subtree have X0<= X, and all nodes in both left and right subtrees have
Y<=Y0. Here X denotes the key value and Y denotes the priority or weights.
1) Key Follows standard BST ordering (left is smaller and right is greater)
2) Priority(or weights) Randomly assigned value that follows Max-Heap property.
21 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
It is simply a binary search tree, therefore to print a sorted order of keys, traverse it in the
same way as we would conventional BSTs. Searching for a treap is similar to searching for
a tree.
1. Insertion in Treap
To insert a new key x into the treap, generate a random priority y for x. Binary search
for x in the tree, and create a new node at the leaf position where the binary search determines a
node for x should exist. Then as long as x is not the root of the tree and has a larger priority
number than its parent z, perform a tree rotation that reverses the parent-child relation
between x and z.
1. Create new node with key equals to x and value equals to a random value.
2. Perform standard BSTinsert.
3. Use rotations to make sure that inserted node's priority follows max heap property.
2. Deletion in Treap
To delete a node x from the treap, remove it if it is a leaf of the tree. If x has a single child, z,
remove x from the tree and make z be the child of the parent of x (or make z the root of the tree
if x had no parent). Finally, if x has two children, swap its position in the tree with its immediate
successor z in the sorted order, resulting in one of the previous cases. In this last case, the swap
may violate the heap-ordering property for z, so additional rotations may need to be performed to
restore this property.
22 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
2. Else replace node's priority with minus infinite ( -INF ), and do appropriate rotations to bring
the node down to a leaf.
5 (90)
/ \
2 (75) 8 (80)
/ \
1(40) 3(50)
Step 1: Delete 2
2 has two children (1 and 3).
Compare priority of left (40) and right (50) → Right child has higher priority.
Left rotation on 2 moves 3 up:
5 (90)
/ \
3 (50) 8 (80)
/
2 (75)
/
1(40)
5 (90)
/ \
23 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
3 (50) 8 (80)
/
1(40)
3. Searching in Treap
To search for a given key value, apply a standard search algorithm in a binary search tree,
ignoring the priorities.
Example:
10 (50)
\
20 (80)
/
15 (30)
10 has priority 50
20 has priority 80 (higher than 10)
15 has priority 30
Since 20 has higher priority than 10, a left rotation at 10 is required.
24 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Step-by-Step Execution
Identify nodes:
x = 10
y = [Link] = 20
T2 = [Link] = 15
Perform rotation:
20 (y) becomes the new root.
10 (x) moves down to become the left child of 20.
15 (T2) becomes the right child of 10.
20 (50)
/
10 (80)
\
15 (30)
----------------------
Node values (key) with random priority values (in brackets):
20 has priority 50
10 has priority 80 (higher than 20)
15 has priority 30
Since 10 has higher priority than 20, a right rotation at 20 is required.
25 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
y = 20
x = [Link] = 10
T2 = [Link] = 15
Perform rotation:
10 (x) becomes the new root.
20 (y) moves down to become the right child of 10.
15 (T2) becomes the left child of 20.
10 (80)
\
20 (50)
/
15 (30)
class TreapNode
{
int key, priority;
TreapNode left, right;
}
class Treap
{
public static TreapNode rightRotate(TreapNode y)
{
TreapNode x = [Link];
TreapNode T2 = [Link];
[Link] = y;
[Link] = T2;
return x;
}
public static TreapNode leftRotate(TreapNode x)
{
TreapNode y = [Link];
TreapNode T2 = [Link];
[Link] = x;
[Link] = T2;
26 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
return y;
}
public static TreapNode newNode(int key)
{
TreapNode temp = new TreapNode();
[Link] = key;
//Generates a random priority (between 0 and 99) using [Link]().
//This priority ensures that the Treap maintains heap properties.
[Link] = (int)([Link]() * 100);
[Link] = [Link] = null;
return temp;
}
public static TreapNode insertNode(TreapNode root, int key)
{
if (root == null)
{
return newNode(key);
}
//If key is smaller than or equal to the current node’s key, insert it into the left subtree.
if (key <= [Link])
{
[Link] = insertNode([Link], key);
//If the newly inserted node (now in [Link]) has a higher priority than the root node:
//Perform a right rotation to bring it up.
if ([Link] > [Link])
{
root = rightRotate(root);
}
}
//If key is greater than the root’s key, insert it into the right subtree.
else
{
[Link] = insertNode([Link], key);
//If the newly inserted node (now in [Link]) has a higher priority than the root:
//Perform a left rotation to bring it up.
if ([Link] > [Link])
{
root = leftRotate(root);
}
}
return root;
}
public static TreapNode deleteNode(TreapNode root, int key)
27 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
{
[Link]("DeleteNode key " + key + " [Link] " + [Link]);
if (root == null)
return root;
28 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
{
if (root == null || [Link] == key)
return root;
if ([Link] < key)
return search([Link], key);
return search([Link], key);
}
static void preorder(TreapNode root)
{
if (root != null)
{
[Link]("key: " + [Link] + " | priority: " + [Link]);
preorder([Link]);
preorder([Link]);
}
}
public static void main(String[] args)
{
Scanner sc = new Scanner([Link]);
int n = [Link]();
int arr[] = new int[n];
for(int i=0;i<n;i++)
{
arr[i] = [Link]();
}
TreapNode root = null;
for(int a:arr)
{
root = insertNode(root,a);
}
preorder(root);
[Link]("Enter item to search ");
int key = [Link]();
TreapNode result = search(root, key);
if(result != null)
{
[Link]("Search result "+ [Link] + " " + [Link]);
}
else
[Link]("Key " + key + " not found");
do
{
[Link]("Enter item to delete ");
29 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
key = [Link]();
root = deleteNode(root, key);
[Link]("After delete");
preorder(root);
}while(key != -1 && root != null);
}
}
Input:-
6
234517
Output:-
key: 2 | priority: 94
key: 1 | priority: 47
key: 7 | priority: 85
key: 5 | priority: 23
key: 4 | priority: 14
key: 3 | priority: 6
(note : here only partial output is shown this is for your understanding purpose)
30 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Given an integer array nums and an integer k, return the kth largest element in the array.
Note that it is the kth largest element in the sorted order, not the kth distinct element.
You must solve it in O(n) time complexity.
Example 1:
Input: nums = [3,2,1,5,6,4], k = 2
Output: 5
Example 2:
Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4
Note: You are suppose to print the K'th largest height in the sorted order of heights[].
Explanation
The inorder traversal of a Binary Search Tree (BST) gives elements in sorted order
(ascending).
To find the k-th largest element, we convert it into finding the (n - k + 1)-th smallest
element.
Formula:
k = n−p+1
o n = Total number of elements.
o p = k-th largest element to find.
o k = Equivalent smallest element.
Example Calculation:
31 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Operation Complexity
Insertion in Treap O(log n) (Expected)
Inorder Traversal O(n) (Worst-case)
Finding k-th element O(k) ≈ O(log n)
[Link]
import [Link].*;
class TreapNode
{
int data;
int priority;
TreapNode left;
TreapNode right;
TreapNode(int data)
{
[Link] = data;
[Link] = new Random().nextInt(1000);
[Link] = [Link] = null;
}
}
class KthLargest
{
static int k;
public static TreapNode rotateLeft(TreapNode root)
{
TreapNode R = [Link];
TreapNode X = [Link];
[Link] = root;
[Link] = X;
return R;
}
public static TreapNode rotateRight(TreapNode root)
{
TreapNode L = [Link];
32 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
TreapNode Y = [Link];
[Link] = root;
[Link] = Y;
return L;
}
public static TreapNode insertNode(TreapNode root, int data){
if (root == null) {
return new TreapNode(data);
}
if (data < [Link])
{
[Link] = insertNode([Link], data);
if ([Link] != null && [Link] > [Link]) {
root = rotateRight(root);
}
}
else {
[Link] = insertNode([Link], data);
if ([Link] != null && [Link] > [Link]) {
root = rotateLeft(root);
}
}
return root;
}
static void inorder(TreapNode root)
{
if (root == null)
return;
inorder([Link]);
k--;
if(k==0){
[Link]([Link]);
return;
}
inorder([Link]);
}
public static void main(String[] args){
Scanner sc = new Scanner([Link]);
int n = [Link]();
int p = [Link]();
k=n-p+1;
int arr[] = new int[n];
33 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
for(int i=0;i<n;i++){
arr[i] = [Link]();
}
TreapNode root = null;
for(int a:arr){
root = insertNode(root,a);
}
inorder(root);
}
}
Example-1:
Input =
63
243165
Output =
4
Example-2:
Input =
62
321564
Output =
5
34 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Applications
Trie Introduction:
The Trie data structure is used to efficiently store and retrieve a set of strings.
It organises strings such that common prefixes are shared among strings, making operations like
searching for words with a given prefix efficient. Trie allows for quick retrieval of all strings with
a given prefix, making it highly efficient for autocomplete and predictive text applications.
A Trie node is a data structure used to construct Trie. Each node contains the following
components:
Links to Child Nodes: A Trie node contains an array of pointers called “links” or “pointer
to children” for each letter of the lowercase alphabet. These pointers represent connections
to child nodes corresponding to each letter of the alphabet. For instance, the link at index 0
corresponds to the child node representing the letter 'a', the link at index 1 corresponds to
'b', and so forth.
Flag for End of Word: Each Trie node contains a boolean flag indicating whether the node
marks the end of a word. This flag is essential for distinguishing between prefixes and
complete words stored in the Trie.
Contains Key: This operation checks whether a specific letter (or key) exists as a child
node of the current Trie node. It returns true if the letter is present, indicating a valid path
in the Trie.
35 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Get Child Node: Given a letter, this operation retrieves the corresponding child node of
the current Trie node. If the letter is present, it returns the pointer to the child node;
otherwise, it returns nullptr, signifying the absence of the letter.
Put Child Node: This operation establishes a connection between the current Trie node
and a child node representing a particular letter. It sets the link at the corresponding index
to point to the provided child node.
Set End Flag: Marks the current Trie node as the end of a word. This flag is crucial for
determining whether a string stored in the Trie terminates at this node, indicating a
complete word.
Is End of Word: Checks whether the current Trie node signifies the end of a word by
examining the end flag. It returns true if the node marks the end of a word; otherwise, it
returns false.
Check if the current node has a child node corresponding to the character.
If not, create a new node and link it as a child of the current node.
Move to the child node corresponding to the character.
Step 3: Once all characters are inserted, mark the end of the word by setting the flag of the last
node to true.
36 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Check if the current node has a child node corresponding to the character.
If not, the word is not in the Trie.
Move to the child node corresponding to the character.
Step 3: After processing all characters, check if the flag of the last node is set to true. If yes, the
word is found; otherwise, it is not.
37 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Check if the current node has a child node corresponding to the character.
If not, there is no word with the given prefix.
Move to the child node corresponding to the character.
Step 3: If all characters of the prefix are found, return true indicating the existence of
words with the given prefix.
38 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
39 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
40 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Let's say the Trie looks like this (simplified tree format):
root
└── s
└── t
└── r
└── i
└── v
├── e └── r (end) ← "striver"
└── i
└── n
└── g (end) ← "striving"
└── n
└── g (end) ← "string"
└── k
└── e (end) ← "strike"
Delete("striving")
� Recursive Steps:
1. Start at root → 's' → 't' → 'r' → 'i' → 'v' → 'i' → 'n' → 'g'
2. We reach 'g' which is the end of "striving":
o flag = true → we unset it → flag = false
3. Now check: does 'g' have any children? → No
o So 'g' can be deleted from 'n'
4. Check 'n': any children left? → No → delete 'n' from 'i'
5. Check 'i': any children left? → 'e' (from "striver") → ❌ Keep 'i'
41 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
root
└── s
└── t
└── r
└── i
└── v
└── e └── r (end) ← "striver"
└── n
└── g (end) ← "string"
└── k
└── e (end) ← "strike"
Pseudocode Summary:
delete(word):
if not search(word): return false
call deleteHelper(root, word, index = 0)
return true
42 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Complexity Analysis
Time Complexity:
Insertion: O(N) where N is the length of the word being inserted. This is because we have to
iterate over each letter of the word to find its corresponding node or create a node accordingly.
Search: O(N) where N is the length of the word being searched for. This is because in Trie search
we traverse over each letter for the word from the root, checking if the current node contains a
node at the index of the next letter. This process repeats until we reach the end of the word or
encounter a node without the next letter.
Prefix Search: O(N) where N is the length of the prefix being searched for. Similar to searching
for words, in prefix search we also iterate over each letter of the word to find its corresponding
node.
Space Complexity: O(N) where N is the total number of characters across all unique words
inserted into the Trie. For each character in a word, a new node may need to be created leading to
space proportional to the number of characters.
Advantages of Trie:
43 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
o
Word frequency tracking
o
Suffix Trie/Tree for substring queries
7. Space-Time Tradeoff Customizable
Tries can be optimized for space using:
o Compressed Trie (Radix Trie)
o Ternary Search Trie
o Suffix Tree (for substrings)
Write a java Program to Implement a Trie Data Structure which supports the following
Four operations:
Search (word): To check if the string `word` is present in the Trie or not.
Insert (word): To insert a string `word` in the Trie.
Start With(word): To check if there is a string that has the prefix `word`.
Delete(word) : To Delete a sting ‘word’ in the Trie.
Example 1:
Enter strings:
striver striving string strike
Strings are inserted in Trie.
Enter a word to search:
striving
True
Enter a prefix to check:
stri
True
Enter a word to delete:
striving
Deleted successfully
import [Link].*;
class Trie
{
// Node structure for Trie
static class Node
{
// Array to store links to child nodes,each index represents a letter
44 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
{
return links[ch - 'a'] != null;
}
// Insert a new node with a specific key (letter) into the Trie
void put(char ch, Node node)
{
links[ch - 'a'] = node;
}
// Get the node with a specific key (letter) from the Trie
Node get(char ch)
{
return links[ch - 'a'];
}
void unsetEnd()
{
flag = false;
}
boolean isEmpty()
{
for (int i = 0; i < 26; i++)
{
if (links[i] != null) return false;
}
return true;
}
}
// Trie class
private final Node root;
45 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
// Inserts a word into the Tri Time Complexity O(len), where len is the length of the word
public void insert(String word)
{
Node node = root;
for (char ch : [Link]())
{
if ()
{
// Create a new node for the letter if not present
[Link](ch, new Node());
}
// Move to the next node
node = [Link](ch);
}
// Mark the end of the word
[Link]();
}
// Returns if there is any word in the trie that starts with the given prefix
public boolean startsWith(String prefix)
{
Node node = root;
for (char ch : [Link]())
{
if ()
{
// If a letter is not found, there is no word with the given prefix
46 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
return false;
}
// Move to the next node
node = [Link](ch);
}
// The prefix is found in the Trie
return true;
}
char ch = [Link](index);
Node next = [Link](ch);
if (next == null)
{
return false;
}
47 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
return false;
}
[Link]("Enter strings:");
String[] str = [Link]().split(" ");
for (String s : str)
{
[Link](s);
}
// Search
[Link]("Enter a word to search:");
String searchWord = [Link]();
[Link]([Link](searchWord) ? "True" : "False");
// Prefix check
[Link]("Enter a prefix to check:");
String prefix = [Link]();
[Link]([Link](prefix) ? "True" : "False");
// Delete
[Link]("Enter a word to delete:");
String deleteWord = [Link]();
boolean deleted = [Link](deleteWord);
[Link](deleted ? "Deleted successfully" : "Word not found");
}
}
48 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Applications:
Given a string text and an array of strings words, return an array of all index pairs [i, j]
so that the substring text[i...j] is in words. Return the pairs [i, j] in sorted order (i.e., sort them by
their first coordinate, and in case of ties sort them by their second coordinate).
Example 1:
Input: text = "thestoryofleetcodeandme", words = ["story","fleet","leetcode"]
Output: [[3,7],[9,13],[10,17]]
Example 2:
Input: text = "ababa", words = ["aba","ab"]
Output: [[0,1],[0,2],[2,3],[2,4]]
Explanation: Notice that matches can overlap, see "aba" is found in [0,2] and [2,4].
Approach:
Step 1: Build a Trie from the words array
Why a Trie?
Since we go from left to right (i = 0 to end), and j increases with i, pairs are naturally in
sorted order.
But if required by constraints, you can sort the list of pairs explicitly by i, then j.
49 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Let:
n = [Link]()
L = average/max word length
k = number of words
Building the Trie:
O(k * L)
Searching in text:
Worst-case O(n * L), as for each i, we may go up to L characters while matching.
So total: O(k * L + n * L) — very efficient compared to a brute-force O(n²).
Java Program for Index Pairs of a String using Trie DS: [Link]
import [Link].*;
50 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
{
// Step 1: Build the Trie
for (String word : words)
insert(word);
if ([Link][idx] == null)
break;
node = [Link][idx];
if ([Link])
{
[Link](new int[]{i, j});
}
j++;
}
}
51 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Example-1:
input=
thestoryofleetcodeandme
story fleet leetcode
output=
[3, 7]
[9, 13]
[10, 17]
Example-2:
input=
abcabcabc
abc abca ab
output=
[0, 1]
[0, 2]
[0, 3]
[3, 4]
[3, 5]
[3, 6]
[6, 7]
[6, 8]
52 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Given an array of strings words, find the longest string in words such that every prefix of it is
also in words.
The string "app" has prefixes "ap" and "a", all of which are in words.
Return the string described above. If there is more than one string with the same length, return
the lexicographically smallest one, and if no string exists, return "".
Example 1:
Output: “kiran”
Explanation: “kiran” has prefixes “kira”, “kir”, “ki”, and “k”, and all of them appear in words.
Example 2:
Output: “apple”
Explanation: Both “apple” and “apply” have all their prefixes in words. However, “apple” is
lexicographically smaller, so we return that.
Example 3:
Output: “ ”
Algorithm/Intuition:
We create a Trie because we need to efficiently check whether every prefix of a given string is
present in the array. Without a Trie, we would need to iterate through the array for each prefix
check, resulting in a time complexity that is exponential in the length of the strings.
By constructing a Trie from the array of words, we can efficiently check whether each prefix
exists in the array. This is because each node in the Trie represents a character, and the paths from
the root to each node represent the prefixes present in the array. Traversing the Trie allows us to
quickly verify whether a given prefix exists.
The longest string will all prefixes refer to the string in a given set that is longest and also has
every prefix present in the same set.
53 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Algorithm 1:
Step 1:Iterate over each word in the input vector and insert each word into the Trie.
Step 2: Initialise an empty string that is set at the complete string initially.
Step 3: Iterate over each word in the input vector again and check if all prefixes of the word exist
in the Trie.
If all prefixes exist: Compare the word’s length and the lexicographical order with the
longest complete string found so far.
Update the longest complete string if current word > global complete string length.
54 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Step 4: If no complete string is found, return None. Otherwise return the longest complete string
found.
Time Complexity:
O(2N) where N is the total number of characters of all unique words hence the time complexity.
Insertion into Trie: O(N) To insert each word into the Trie, we iterate over all its
characters and insert them by creating nodes. To insert all words, the time complexity
becomes O(N).
Finding Longest Complete String: O(N): Involves iterating over each word in the input
vector and checking if all prefixes in the Trie exist. Hence the time complexity for this is
also O(N).
Space Complexity: O(N) where N is the total number of characters of all unique words hence the
time complexity.
Each node of the Trie contains an array of pointers to child nodes, which typically takes
constant 26 pointers for lowercase English letters.
The number of nodes to be created will be proportional to the number of characters in all
unique words hence O(N).
55 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Java Program for Longest word with all prefixes using Trie DS: [Link]
import [Link].*;
class Node
{
Node[] links;
// Array to hold links to child nodes for each character
boolean flag;
// Flag to indicate if it marks the end of a word
56 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
class Trie
{
// Root node of the Trie
private Node root;
57 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
[Link](word);
58 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
longest = word;
}
}
}
// Return "None" if no complete string found
if ([Link](""))
return "None";
// Return the longest complete string
return longest;
}
Example-1:
input = k kmi km kmit kme kmec ksj ksjc ks kmecs
output = "kmecs"
Example-2:
input = t tanker tup tupl tu tuple tupla
output = "tupla"
Example-3:
input = abc bc ab abcd
output = ""
59 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
We are given an array of strings words and an integer k, we have to return k strings which has
highest frequency.
We need to return the answer which should be sorted by the frequency from highest to lowest and
the words which has the same frequency sort them by their alphabetical order.
Example-1:
Input:
words = ["i","love","writing","i","love","coding"], k = 2
Output:
["i","love"]
Explanation: "i" and "love" are the two most frequent words.
Example-2:
Input:
words = ["it","is","raining","it","it","raining","it","is","is"], k = 3
Output:
["it","is","raining"]
Approach:
[Link] Frequencies: Use a HashMap<String, Integer> to count the frequency of each word.
[Link] into Trie: Insert each unique word into a Trie, and store the frequency at the end node of
each word.
[Link] on Trie: Traverse the Trie using DFS to collect words and their frequencies.
60 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
import [Link].*;
class TrieNode
{
TrieNode[] children;
boolean isEndOfWord;
int frequency; // store frequency at end node
String word; // store the word itself at end node for reference
TrieNode()
{
children = new TrieNode[26];
isEndOfWord = false;
frequency = 0;
word = null;
}
}
class Trie
{
TrieNode root;
Trie()
{
root = new TrieNode();
}
61 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
if ([Link])
{
[Link](new StringFrequency([Link], [Link]));
if ([Link]() > k) {
[Link](); // remove lowest priority element
}
}
class StringFrequency
{
String word;
int freq;
62 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
{
// Step 1: Count frequency
Map<String, Integer> freqMap = new HashMap<>();
for (String word : words) {
[Link](word, [Link](word, 0) + 1);
}
return result;
}
public static void main(String[] args)
{
Scanner sc=new Scanner([Link]);
String dict[]=[Link]().split(",");
int k=[Link]();
[Link](new FrequentWord().topKFrequent(dict,k));
}
}
Example-1:
input =
ball,are,case,doll,egg,case,doll,egg,are,are,egg,case,are,egg,are,case
3
output =
[are, case, egg]
Example-2:
input =
ball,are,case,doll,egg,case,doll,egg,are,are,egg,case,are,egg,are
3
output =
[are, egg, case]
63 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Suffix Tree:
Suffix tree is a compressed trie of all the suffixes of a given string. Suffix trees help in solving a
lot of string related problems like pattern matching, finding distinct substrings in a given string,
finding longest palindrome etc. In this following points will be covered:
Compressed Trie
Suffix Tree
Before going to suffix tree, let's first try to understand what a compressed trie is.
Consider the following set of strings: {"banana","nabd","bcdef","bcfeg","aaaaaa","aaabaa" }
A standard trie for the above set of strings will look like:
64 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
As it might be clear from the images show above, in a compressed trie, edges that direct to a
node having single child are combined together to form a single edge and their edge labels are
concatenated.
So this means that each internal node in a compressed trie has atleast two children. Also it has
atmost N leaves, where N is the number of strings inserted in the compressed trie.
Now both the facts: Each internal node having atleast two children, and that there are N leaves,
implies that there are atmost 2N−1 nodes in the trie.
So the space complexity of a compressed trie is O(N) as compared to the O(N2) of a normal trie.
So that is one reason why to use compressed tries over normal tries.
Before going to construction of suffix trees, there is one more thing that should be understood,
Implicit Suffix Tree. In Implicit suffix trees, there are atmost N leaves, while in normal one there
should be exactly N leaves. The reason for atmost N leaves is one suffix being prefix of another
suffix.
65 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
To avoid getting an Implicit Suffix Tree we append a special character that is not equal to any other
character of the string. Suppose we append $ to the given string then, so the new string is "banana$".
Now its suffix tree will be
66 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
If many keys share long prefixes but then diverge quickly, substrings are stored
redundantly on different edges.
Although they save space over regular tries in general, they don't beat suffix trees in
substring-heavy datasets.
5. Harder to Implement
Suffix tree as mentioned previously is a compressed trie of all the suffixes of a given string, so the
brute force approach will be to consider all the suffixes of the given string as separate strings and
insert them in the trie one by one
A suffix tree is a data structure that is used to store all the suffixes of a given string in an
efficient manner. It can be used to solve a wide range of string problems in linear time
complexity, making it a powerful tool in competitive programming.
To build a suffix tree, we can start by creating a root node and adding edges to represent
each character in the input string. Then, we add additional edges to represent all possible suffixes
of the string, splitting the edges wherever there is a branching in the suffixes. This process
continues until all suffixes have been added.
Once the suffix tree has been built, we can use it to search for substrings in the original
string. We can start at the root of the tree and traverse the edges based on the characters in the
67 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
substring we are searching for. If we can successfully traverse all the characters in the substring,
we have found a match.
Suffix trees are very useful for solving a variety of string-related problems, such as finding
the longest repeated substring in a string or finding all occurrences of a pattern in a text. They are
also used in bioinformatics to analyze DNA sequences.
Here are some important concepts and operations/applications related to suffix trees:
Construction: A suffix tree can be constructed from a given string using various algorithms such
as Ukkonen's algorithm. These algorithms use a concept called implicit suffix tree, where the tree
is constructed as suffixes are added one by one.
Searching: Once a suffix tree is constructed, it can be used to search for a given pattern in the
string. This is done by traversing the tree based on the characters in the pattern. If the pattern is
found, the tree can be used to find all the occurrences of the pattern in the string.
Longest common substring: A suffix tree can be used to find the longest common substring
between two strings. This is done by first constructing a suffix tree for both strings and then
finding the deepest node in the tree that has children from both strings.
Longest repeated substring: A suffix tree can also be used to find the longest repeated substring
in a given string. This is done by finding the deepest node in the tree that has more than one leaf
node.
Counting substrings: A suffix tree can be used to count the number of occurrences of all
substrings of a given string in linear time complexity. This is done by counting the number of leaf
nodes in the subtree rooted at each internal node.
These are some of the important concepts and operations related to suffix trees in competitive
programming.
A suffix tree is a compressed trie-like data structure used to represent all suffixes of a given
string. It allows you to perform fast string operations.
Time
Operation
Complexity
68 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Time
Operation
Complexity
Where:
banana$
anana$
nana$
ana$
na$
a$
Each suffix becomes a path in the tree. The $ is added to make sure each
suffix ends uniquely.
Trie Structure (Character-wise)
69 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Example:
import [Link].*;
class SuffixTrie
{
static final int NUM_CHARS = 26;
// SuffixTrie node
static class SuffixTrieNode
{
SuffixTrieNode[] children = new SuffixTrieNode[NUM_CHARS];
SuffixTrieNode()
{
isEndOfWord = false;
for (int i = 0; i < NUM_CHARS; i++)
children[i] = null;
}
};
70 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
currentNode = [Link][index];
}
// print SuffixTrie
static void print(SuffixTrieNode root, char[] str, int level)
{
// If node is leaf node, it indicates end of string,
// so a null character is added and string is printed
if (isLeafNode(root))
{
for (int k = level; k < [Link]; k++)
str[k] = 0;
[Link](str);
}
int i;
for (i = 0; i < NUM_CHARS; i++)
{
// if NON NULL child is found add parent key to str and
// call the print function recursively for child node
if ([Link][i] != null)
{
str[level] = (char) (i + 'a');
71 | P a g e KMIT (AUTONOMOUS)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (RKR21 Regulations)
Banana
Output:
word banana
word anana
word nana
word ana
word na
word a
a
ana
anana
banana
na
nana
72 | P a g e KMIT (AUTONOMOUS)