0% found this document useful (0 votes)
11 views57 pages

Cpunit Iii

Uploaded by

Sai someone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views57 pages

Cpunit Iii

Uploaded by

Sai someone
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 57

COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

SYLLABUS:-

Tree Algorithms: Fenwick Tree, Segment Tree – Applications-Range Sum Queries. Treap-
Applications- Kth Largest Element in an Array.

Trie: Introduction, Suffix Tree, Applications-Index Pairs of a String, Longest word with all
prefixes, top K frequent words.

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
M.Fenwick 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)

A straightforward implementation of the above would look like this.

int a[] = {2, 1, 4, 6, -1, 5, -32, 0, 1};


void update(int i, int v) //assigns value v to a[i]
{
a[i] = v;
}
int prefixsum(int k) //calculate the sum of all a[i] such that 0 <= i < k
{
int sum = 0;
for(int i = 0; i < k; i++)
sum += a[i];
return sum;
}

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.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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?

x&(-x) gives the last set bit in a number x. How?

Example:-

x = 10(in decimal) = 1010(in binary)

The last set bit is given by x&(-x) = (10)1(0) & (01)1(0) = 0010 = 2(in decimal)

Basic Idea of Binary Indexed Tree:

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.

Let’s use an example to understand how BIT[] stores partial sums.

//for ease, we make sure our given array is 1-based indexed

int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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

BIT[X]={ a[x] -----if x is odd

a[1]+……….+a[x] ------- if x is power of 2 }

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

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

5 + 2 + 9 + (-3) + 5 + 20 + 10 = 48 in traditional way, but by using fenwick tree we can do


this in minimum time. The following method demonstrates that Range Sum Calculation.

int sum(int i)
{
int sum = 0;
while ( i > 0 )
{
sum +=BIT[i];
i -= i & -i;//flip the last set bit
}
return sum;
}

Example: - to find sum from the range 1 to 7 we use

Sum(7) = sum(00111)

= BIT[00111] + BIT[00110] +BI T[00100]

= BIT[7] +BIT[6] + BIT[4]

= range(7,7) + range(5,6) + range(1,4)

= 10 + 25 + 12 =48

To compute the sum (7) while loop iterates only three times

Sum (8) = sum (01000)

= BIT [01000]

= BIT [8]

=range (8,8) =41

To compute sum(8) while loop iterates only once.This 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.

void add(int i, int k)


{
while (i < T.length)
{
T[i] += k;

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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

BIT[10000] is out of array index, function holds.

Java Program to implement Fenwick tree: FenWickTree.java

import java.util.*;
class FenWickTree
{
int[] nums;
int[] BIT;
int n;
public FenWickTree(int[] nums)
{
this.nums = nums;
n = nums.length;
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);
}

public int getSum(int i)

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

{
int sum = 0;
i++;
while (i > 0)
{
sum += BIT[i];
i -= (i & -i);

}
return sum;
}

public int sumRange(int i, int j)


{
return getSum(j) - getSum(i - 1);
}

public static void main(String args[] )


{
Scanner scan = new Scanner(System.in);
int n=scan.nextInt();
int q=scan.nextInt();
int[] nums=new int[n];
for(int i=0; i<n; i++)
{
nums[i] = scan.nextInt();
}
FenWickTree ft =new FenWickTree(nums);
while(q-->0)
{
int opt=scan.nextInt();
if(opt==1)
{
int s1 = scan.nextInt();
int s2 = scan.nextInt();
System.out.println(ft.sumRange(s1,s2));
}
else
{
int ind = scan.nextInt();

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

int val= scan.nextInt();


ft.update(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

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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.

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.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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.

The segtree of array A of size 7 will look like :

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Applications: - Segment trees are used to find range sum queries.

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 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.

void build(int node, int start, int end)


{
if(start == end)
{
// Leaf node will have a single element
tree[node] = A[start];
}
else
{
int mid = (start + end) / 2;
// Recurse on the left child
build(2*node, start, mid);
// Recurse on the right child
build(2*node+1, mid+1, end);
// Internal node will have the sum of both of its children
tree[node] = tree[2*node] + tree[2*node+1];
}
}
In the above code we will start from the root and recurse on the left and the right child
until we reach the leaves. From the leaves we will go back to the root and update all the
nodes in the path. node represent the current node we are processing. Since segment tree is a
binary tree. 2*node will represent the left node and 2*node + 1 represent the right
node. start and end represents the interval represented by the node. Complexity of build()
is O(N).

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

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
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, start, mid, idx, val);
}
else
{
// if idx is in the right child, recurse on the right child
update(2*node+1, 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).

To query on a given range, we need to check 3 conditions.

1. range represented by a node is completely inside the given range


2. range represented by a node is completely outside the given range
3. range represented by a node is partially inside and partially outside the given range

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).

int query(int node, int start, int end, int l, int r)


{
if(r < start or end < l)
{
// range represented by a node is completely outside the given range
return 0;
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

if(l <= start and end <= r)


{
// range represented by a node is completely inside the given range
return tree[node];
}
// range represented by a node is partially inside and partially outside the given range
int mid = (start + end) / 2;
int p1 = query(2*node, start, mid, l, r);
int p2 = query(2*node+1, mid+1, end, l, r);
return (p1 + p2);
}

Updating an interval ( Lazy Propagation ):

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, start, mid, l, r, val);
updateRange(node*2 + 1, mid + 1, end, l, r, val);
// Use the result of children calls to update this node
tree[node] = tree[node*2] + tree[node*2+1];
}

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.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

To update an interval we will keep 3 things in mind.

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

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] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += 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] += val;
lazy[node*2+1] += val;
}
return;
}
int mid = (start + end) / 2;
updateRange(node*2, start, mid, l, r, val); // Updating left child
updateRange(node*2 + 1, mid + 1, end, l, r, val); // Updating right child
tree[node] = tree[node*2] + tree[node*2+1]; // Updating root with max value
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

int queryRange(int node, int start, int end, int l, int r)


{
if(start > end or start > r or end < l)
return 0; // Out of range
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] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += lazy[node]; // Mark child as lazy
}
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, start, mid, l, r); // Query left child
int p2 = queryRange(node*2 + 1, mid + 1, end, l, r); // Query right child
return (p1 + p2);
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Java program for segment tree: SegmentTree.java

import java.util.*;
public class SegmentTree
{
class SegmentTreeNode
{
int start, end;
SegmentTreeNode left, right;
int sum;

public SegmentTreeNode(int start, int end)


{
this.start = start;
this.end = end;
this.left = null;
this.right = null;
this.sum = 0;
}
}

SegmentTreeNode root = null;

public SegmentTree(int[] nums)


{
root = buildTree(nums, 0, nums.length-1);
printTree(root);
}

public void printTree(SegmentTreeNode root)


{
if(root == null)
return;
printTree(root.left);
printTree(root.right);
}

private SegmentTreeNode buildTree(int[] nums, int start, int end)


{
if (start > end)
{
return null;
}
else
{
SegmentTreeNode ret = new SegmentTreeNode(start, end);
if (start == end)
KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)
COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

{
ret.sum = nums[start];
}
else
{
int mid = start + (end - start) / 2;
ret.left = buildTree(nums, start, mid);
ret.right = buildTree(nums, mid + 1, end);
ret.sum = ret.left.sum + ret.right.sum;
}
return ret;
}
}

void update(int i, int val)


{
update(root, i, val);
}

void update(SegmentTreeNode root, int pos, int val)


{

if (root.start == root.end)
{
root.sum = val;
}
else
{
int mid = root.start + (root.end - root.start) / 2;
if (pos <= mid)
{
update(root.left, pos, val);
}
else
{
update(root.right, pos, val);
}
root.sum = root.left.sum + root.right.sum;
}
printTree(root);
}

public int sumRange(int i, int j)


{
return sumRange(root, i, j);
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

public int sumRange(SegmentTreeNode root, int start, int end)


{
if (root.start == start && root.end == end)
{
return root.sum;
}
else
{
int mid = root.start + (root.end - root.start) / 2;
if (end <= mid)
{
return sumRange(root.left, start, end);
}
else if (start >= mid+1)
{
return sumRange(root.right, start, end);
}
else
{
return sumRange(root.left, start, mid) +
sumRange(root.right, mid+1, end);
}
}
}
}

class SegmentTreeTest
{
public static void main(String args[] )
{
Scanner scan = new Scanner(System.in);
int n=scan.nextInt();
int q=scan.nextInt();
int[] nums=new int[n];
for(int i=0; i<n; i++)
{
nums[i] = scan.nextInt();
}
SegmentTree st = new SegmentTree(nums);
while(q != 0)
{
int opt=scan.nextInt();
if(opt==1)

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

{
int s1 = scan.nextInt();
int s2 = scan.nextInt();
System.out.println(st.sumRange(s1,s2));
}
else
{
int ind = scan.nextInt();
int val= scan.nextInt();
st.update(ind, val);
}
q--;
}
}
}

Sample input :-
85
4 2 13 4 25 16 17 8
126
107
2 2 18
2 4 17
127
Output :-
75
89
80

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 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.

Every node of Treap maintains two values.

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.

The following are the features of Treap Datastructure.

 It is a randomized data structure


 Because of the randomized weights, there is a strong possibility that the tree will be
balanced regardless of the sequence in which we add, remove, etc.
 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.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Following are the algorithms for basic operations on Treap:

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.

1. If node to be deleted is a leaf, delete it.

2. Else replace node's priority with minus infinite ( -INF ), and do appropriate rotations to
bring the node down to a leaf.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

3. Searching in Treap

To search for a given key value, apply a standard search algorithm in a binary search
tree, ignoring the priorities.

public Node search(Node root, int key)


{
// Base Cases: root is null or key is present at root
if (root==null || root.key==key)
return root;
// Key is greater than root's key
if (root.key < key)
return search(root.right, key);
// Key is smaller than root's key
return search(root.left, key);
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Java program for Treap: Treap.java


import java.util.*;

class TreapNode
{
int key, priority;
TreapNode left, right;
}
class Treap
{
public static TreapNode rightRotate(TreapNode y)
{
TreapNode x = y.left;
TreapNode T2 = x.right;
x.right = y;
y.left = T2;
return x;
}
public static TreapNode leftRotate(TreapNode x)
{
TreapNode y = x.right;
TreapNode T2 = y.left;
y.left = x;
x.right = T2;
return y;
}
public static TreapNode newNode(int key)
{
TreapNode temp = new TreapNode();
temp.key = key;
temp.priority = (int)(Math.random() * 100);
temp.left = temp.right = null;
return temp;
}
public static TreapNode insertNode(TreapNode root, int key)
{
if (root == null)
{
return newNode(key);
}
if (key <= root.key)
{
root.left = insertNode(root.left, key);
if (root.left.priority > root.priority)
{
root = rightRotate(root);
}
}
else

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

{
root.right = insertNode(root.right, key);
if (root.right.priority > root.priority)
{
root = leftRotate(root);
}
}
return root;
}
public static TreapNode deleteNode(TreapNode root, int key)
{
System.out.println("DeleteNode key " + key + " root.key " + root.key);
if (root == null)
return root;
if (key < root.key)
root.left = deleteNode(root.left, key);
else if (key > root.key)
root.right = deleteNode(root.right, key);
else if (root.left == null)
{
TreapNode temp = root.right;
root = temp;
}
else if (root.right == null)
{
TreapNode temp = root.left;
root = temp;
}
else if (root.left.priority < root.right.priority)
{
root = leftRotate(root);
root.left = deleteNode(root.left, key);
}
else
{
root = rightRotate(root);
root.right = deleteNode(root.right, key);
}
return root;
}
public static TreapNode search(TreapNode root, int key)
{
if (root == null || root.key == key)
return root;
if (root.key < key)
return search(root.right, key);
return search(root.left, key);
}
static void preorder(TreapNode root)
{

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

if (root != null)
{
System.out.println("key: " + root.key + " | priority: " + root.priority);
preorder(root.left);
preorder(root.right);
}
}
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int arr[] = new int[n];
for(int i=0;i<n;i++)
{
arr[i] = sc.nextInt();
}
TreapNode root = null;
for(int a:arr)
{
root = insertNode(root,a);
}
preorder(root);
System.out.println("Enter item to search ");
int key = sc.nextInt();
TreapNode result = search(root, key);
if(result != null)
{
System.out.println("Search result "+ result.key + " " + result.priority);
}
else
System.out.println("Key " + key + " not found");
do
{
System.out.println("Enter item to delete ");
key = sc.nextInt();
root = deleteNode(root, key);
System.out.println("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

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

key: 3 | priority: 6

Enter item to search


2
Search result 2 94
Enter item to delete
2
After delete
key: 7 | priority: 85
key: 1 | priority: 47
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)

Kth Largest Element in an Array Using Treap:

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[].

Not the K'th distinct height.

Explanation

 In the first example, we can see that k=2, so we have to find the second largest
element in this array. If we rearrange the array in descending
order [9,7,6,4,3,2,1][9,7,6,4,3,2,1], we can see that the element 7 is in the second
position, so it is considered the second largest element.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

 In the second example, we can see that k=4, so we have to find the fourth largest
element in this array. If we rearrange the array in descending
order [90,65,34,12][90,65,34,12], we can see that the element 12 is in the fourth
position, so it is considered the fourth largest element.

Java Program for Kth Largest Element in an Array Using Treap:

KthLargest.java
import java.util.*;
class TreapNode
{
int data;
int priority;
TreapNode left;
TreapNode right;
TreapNode(int data)
{
this.data = data;
this.priority = new Random().nextInt(1000);
this.left = this.right = null;
}
}
class KthLargest
{
static int k;
public static TreapNode rotateLeft(TreapNode root)
{
TreapNode R = root.right;
TreapNode X = root.right.left;
R.left = root;
root.right = X;
return R;
}
public static TreapNode rotateRight(TreapNode root)
{
TreapNode L = root.left;
TreapNode Y = root.left.right;
L.right = root;
root.left = Y;
return L;

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

}
public static TreapNode insertNode(TreapNode root, int data){
if (root == null) {
return new TreapNode(data);
}
if (data < root.data)
{
root.left = insertNode(root.left, data);
if (root.left != null && root.left.priority > root.priority) {
root = rotateRight(root);
}
}
else {
root.right = insertNode(root.right, data);
if (root.right != null && root.right.priority > root.priority) {
root = rotateLeft(root);
}
}

return root;
}
static void inorder(TreapNode root)
{
if (root == null)
return;
inorder(root.left);
k--;
if(k==0){
System.out.print(root.data);
return;
}
inorder(root.right);
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int p = sc.nextInt();
k=n-p+1;
int arr[] = new int[n];
for(int i=0;i<n;i++){

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

arr[i] = sc.nextInt();
}
TreapNode root = null;
for(int a:arr){
root = insertNode(root,a);
}
inorder(root);
}
}

Test case=1
Input =
63
243165
Output =
4

Test case=2
Input =
62
321564
Output =
5

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Trie Data Structure:


Introduction, Suffix Tree,

Applications

1. Index Pairs of a String


2. Longest word with all prefixes
3. Top K frequent words.

Trie Introduction:

 Trie data structure is defined as a Tree based data structure that is used for storing some
collection of strings and performing efficient search operations on them.

 The word Trie is derived from reTRIEval, which means finding something or obtaining it.

 Trie follows some property that If two strings have a common prefix then they will have
the same ancestor in the trie.

 A trie can be used to sort a collection of strings alphabetically as well as search


whether a string with a given prefix is present in the trie or not.

 A Trie data structure is used for storing and retrieval of data and the same operations
could be done using another data structure which is Hash Table but Trie can perform
these operations more efficiently than a Hash Table.

 Moreover, Trie has its own advantage over the Hash table. A Trie data structure can be
used for prefix-based searching whereas a Hash table can’t be used in the same way.

Advantages of Trie Data Structure over a Hash Table:


 We can efficiently do prefix search (or auto-complete) with Trie.

 We can easily print all words in alphabetical order which is not easily possible with
hashing.

 There is no overhead of Hash functions in a Trie data structure.

 Searching for a String even in the large collection of strings in a Trie data structure can be
done in O(L) Time complexity, Where L is the number of words in the query string. This
searching time could be even less than O(L) if the query string does not exist in the trie.

Properties of a Trie Data Structure:

 Now we already know that Trie has a tree-like structure. So, it is very important to
know its properties. Below are some important properties of the Trie data structure:

 There is one root node in each Trie.


 Each node of a Trie represents a string and each edge represents a character.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

 Every node consists of hashmaps or an array of pointers, with each index representing a
character and a flag to indicate if any string ends at the current node.
 Trie data structure can contain any number of characters including alphabets, numbers,
and special characters. But for this article, we will discuss strings with characters a-z.
Therefore, only 26 pointers need for every node, where the 0th index represents ‘a’ and
the 25th index represents ‘z’ characters.
 Each path from the root to any node represents a word or string.

Below is a simple example of Trie data structure.

How does Trie Data Structure work?

We already know that the Trie data structure can contain any number of characters
including alphabets, numbers, and special characters. But for this example, we will discuss
strings with characters a-z. Therefore, only 26 pointers need for every node, where the 0th
index represents ‘a’ and the 25th index represents ‘z’ characters.

Any lowercase English word can start with a-z, then the next letter of the word could
be a-z, the third letter of the word again could be a-z, and so on. So for storing a word, we
need to take an array (container) of size 26 and initially, all the characters are empty as there
are no words and it will look as shown below.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Let’s see how a word “and” and “ant” is stored in the Trie data structure:

1. Store “and” in Trie data structure:

 The word “and” starts with “a“, So we will mark the position “a” as filled in the
Trie node, which represents the use of “a”.
 After placing the first character, for the second character again there are 26
possibilities, So from “a“, again there is an array of size 26, for storing the 2nd
character.
 The second character is “n“, So from “a“, we will move to “n” and mark “n” in
the 2nd array as used.
 After “n“, the 3rd character is “d“, So mark the position “d” as used in the
respective array.
2. Store “ant” in the Trie data structure:

 The word “ant” starts with “a” and the position of “a” in the root node has already
been filled. So, no need to fill it again, just move to the node ‘a‘ in Trie
 For the second character ‘n‘ we can observe that the position of ‘n’ in the ‘a’ node
has already been filled. So, no need to fill it again, just move to node ‘n’ in Trie.
 For the last character ‘t‘ of the word, The position for ‘t‘ in the ‘n‘ node is not
filled. So, filled the position of ‘t‘ in ‘n‘ node and move to ‘t‘ node.
After storing the word “and” and “ant” the Trie will look like this:

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Representation of Trie Node:

Every Trie node consists of a character pointer array or hash map and a flag to
represent if the word is ending at that node or not. But if the words contain only lower-case
letters (i.e. a-z), then we can define Trie Node with an array instead of a hash map.

public class TrieNode


{
public TrieNode[] children;
public int wordCount;

public TrieNode()
{
children = new TrieNode[26];
// This will keep track of number of strings that
// are stored in the Trie from root node to any Trie
// node.
wordCount = 0;

}
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Basic Operations on Trie Data Structure:


1. Insertion.
2. Search.
3. Deletion.

1. Insertion in Trie Data Structure:

This operation is used to insert new strings into the Trie data structure. Let us see how this
works:

Let us try to Insert “and” & “ant” in this Trie:

From the above representation of insertion, we can see that the word “and” & “ant” have
shared some common node (i.e “an”) this is because of the property of the Trie data structure
that If two strings have a common prefix then they will have the same ancestor in the trie.

Now let us try to Insert “dad” & “do”:

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Implementation of Insertion in Trie data structure:


Algorithm:

 Define a function insert(TrieNode *root, string &word) which will take two parameters
one for the root and the other for the string that we want to insert in the Trie data
structure.

 Now take another pointer currentNode and initialize it with the root node.

 Iterate over the length of the given string and check if the value is NULL or not in the
array of pointers at the current character of the string.

 If It’s NULL then, make a new node and point the current character to

this newly created node.

 Move the curr to the newly created node.

 Finally, increment the wordCount of the last currentNode, this implies that there is a
string ending currentNode.

Below is the implementation of the above algorithm:

static void insert(TrieNode root, String key)


{
// Initialize the currentNode pointer with the root node
TrieNode currentNode = root;

for (int i = 0; i < key.length(); i++) {


int index = key.charAt(i) - 'a';

// Check if the node exist for the current


// character in the Trie.
if (currentNode.childNode[index] == null) {

// Keep the reference for the newly created


// node.
currentNode.childNode[index] = new TrieNode();
}

// Now, move the current node pointer to the newly


// created node.
currentNode = currentNode.childNode[index];
}

// Increment the wordEndCount for the last currentNode

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

// pointer this implies that there is a string ending at


// currentNode.
currentNode.wordCount++;
}

2. Searching in Trie Data Structure:

Search operation in Trie is performed in a similar way as the insertion operation but
the only difference is that whenever we find that the array of pointers in curr node does not
point to the current character of the word then return false instead of creating a new node
for that current character of the word.
This operation is used to search whether a string is present in the Trie data structure or not.
There are two search approaches in the Trie data structure.
1. Find whether the given word exists in Trie.
2. Find whether any word that starts with the given prefix exists in Trie.
There is a similar search pattern in both approaches. The first step in searching a given
word in Trie is to convert the word to characters and then compare every character with the
trie node from the root node. If the current character is present in the node, move forward to
its children. Repeat this process until all characters are found.

Searching Prefix in Trie Data Structure:

Search for the prefix “an” in the Trie Data Structure.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

public boolean isPrefixExist(TrieNode root, String key)


{
// Initialize the currentNode pointer
// with the root node
TrieNode currentNode = root;
// Iterate across the length of the string
for (char c : key.toCharArray()) {
// Check if the node exist for the current
// character in the Trie.
if (currentNode.childNode == null) {
// Given word as a prefix does not exist in Trie
return false;
}
// Move the currentNode pointer to the already
// existing node for current character.
currentNode = currentNode.childNode;
}
// Prefix exist in the Trie
return true;
}

Searching complete word in Trie Data Structure:

It is similar to prefix search but additionally, we have to check if the word is ending at the last
character of the word or not.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Implementation of Search in Trie data structure:

// Returns true if key presents in trie, else false


static boolean search(TrieNode root, String key)
{
// Initialize the currentNode
// with the root node
TrieNode currentNode = root;
for (int i = 0; i < key.length(); i++)
{
int index = key.charAt(i) - 'a';
// Check if the node exist for the current
// character in the Trie.
if (currentNode.childNode[index] == null)
return false;
// Move the currentNode to the already
// existing node for current character.
currentNode = currentNode.childNode[index];
}
return (currentNode.isEndOfWord);
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

3. Deletion in Trie Data Structure:

This operation is used to delete strings from the Trie data structure. There are three cases
when deleting a word from Trie.

1. The deleted word is a prefix of other words in Trie.


2. The deleted word shares a common prefix with other words in Trie.
3. The deleted word does not share any common prefix with other words in Trie.
1. The deleted word is a prefix of other words in Trie.

As shown in the following figure, the deleted word “an” share a complete prefix with another
word “and” and “ant“.

An easy solution to perform a delete operation for this case is to just decrement the
wordCount by 1 at the ending node of the word.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

2. The deleted word shares a common prefix with other words in Trie.

 As shown in the following figure, the deleted word “and” has some common prefixes
with other words ‘ant’. They share the prefix ‘an’.

 The solution for this case is to delete all the nodes starting from the end of the prefix
to the last character of the given word.

3. The deleted word does not share any common prefix with other words in Trie.

As shown in the following figure, the word “geek” does not share any common prefix with
any other words. The solution for this case is just to delete all the nodes.

Below is the implementation that handles all the above cases:

bool delete_key(TrieNode* root, string& word)


{
TrieNode* currentNode = root;
TrieNode* lastBranchNode = NULL;
char lastBrachChar = 'a';

for (auto c : word) {


if (currentNode->childNode == NULL) {
return false;
}
else {

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

int count = 0;
for (int i = 0; i < 26; i++) {
if (currentNode->childNode[i] != NULL)
count++;
}

if (count > 1) {
lastBranchNode = currentNode;
lastBrachChar = c;
}
currentNode = currentNode->childNode;
}
}

int count = 0;
for (int i = 0; i < 26; i++) {
if (currentNode->childNode[i] != NULL)
count++;
}

// Case 1: The deleted word is a prefix of other words


// in Trie.
if (count > 0) {
currentNode->wordCount--;
return true;
}

// Case 2: The deleted word shares a common prefix with


// other words in Trie.
if (lastBranchNode != NULL) {
lastBranchNode->childNode[lastBrachChar] = NULL;
return true;
}
// Case 3: The deleted word does not share any common
// prefix with other words in Trie.
else {
root->childNode[word[0]] = NULL;
return true;
}
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Java Pogram to implement Trie Data Structure: Trie.java

import java.util.*;
class Trie
{
static final int NUM_CHARS = 26;
static boolean isDeleted = false;
static class TrieNode
{
TrieNode[] children = new TrieNode[NUM_CHARS];
boolean isEndOfWord;
TrieNode()
{
isEndOfWord = false;
for (int i = 0; i < NUM_CHARS; i++)
children[i] = null;
}
};
static TrieNode root;
static void insert(String key)
{
int level;
int length = key.length();
int index;
TrieNode currentNode = root;
for (level = 0; level < length; level++)
{
index = key.charAt(level) - 'a';
if (currentNode.children[index] == null)
currentNode.children[index] = new TrieNode();
currentNode = currentNode.children[index];
}
currentNode.isEndOfWord = true;
}

static boolean search(String key)


{
int level;
int length = key.length();
int index;
TrieNode currentNode = root;
for (level = 0; level < length; level++)
{
index = key.charAt(level) - 'a';

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

if (currentNode.children[index] == null)
return false;
currentNode = currentNode.children[index];
}
return (currentNode.isEndOfWord);
}
static boolean isEmpty(TrieNode root)
{
for (int i = 0; i < NUM_CHARS; i++)
if (root.children[i] != null)
return false;
return true;
}

static TrieNode delete(TrieNode root, String key, int depth)


{
if (root == null)
return null;
if (depth == key.length())
{
isDeleted = root.isEndOfWord;
if (root.isEndOfWord)
root.isEndOfWord = false;
if (isEmpty(root))
{
return null;
}
return root;
}

int index = key.charAt(depth) - 'a';


if (root.children[index] == null)
return null;
root.children[index] = delete(root.children[index], key, depth + 1);
if (isEmpty(root) && root.isEndOfWord == false)
{
return null;
}
return root;
}

static boolean isLeafNode(TrieNode root)


{

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

return root.isEndOfWord == true;


}

static void print(TrieNode root, char[] str, int level)


{
if (isLeafNode(root))
{
for (int k = level; k < str.length; k++)
str[k] = 0;
System.out.println(str);
}
int i;
for (i = 0; i < NUM_CHARS; i++)
{
if (root.children[i] != null)
{
str[level] = (char) (i + 'a');
print(root.children[i], str, level + 1);
}
}
}

public static void main(String args[])


{
Scanner sc=new Scanner(System.in);
String keys[]=sc.nextLine().split(" ");
root = new TrieNode();
int i;
for (i = 0; i < keys.length ; i++)
insert(keys[i]);
char[] str = new char[50];
System.out.println("Content of Trie: ");
print(root, str, 0);
while(true)
{
System.out.println("Enter a word to search");
String word = sc.next();
if(word.equals("-1"))
break;
if(search(word) == true)
System.out.println(word + " is present ");
else System.out.println(word + " is not present");
}
while(root != null)

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

{
System.out.println("Enter a word to delete");
String word = sc.next();
if(word.equals("-1"))
break;
if(delete(root, word, 0) != null & isDeleted == true)
{
System.out.println(word + " is deleted ");
}

else
System.out.println(word + " is not present in Trie to be deleted");
System.out.println("Content of Trie after deletion: ");
print(root, str, 0);
}
}
}

Sample Input:
bell bellbottom bellbrand bellring
output: Content of Trie:
bell
bellbottom
bellbrand
bellring
Enter a word to search
bell
bell is present
Enter a word to search
-1
Enter a word to delete
bell
bell is deleted
Content of Trie after deletion:
bellbottom
bellbrand
bellring
Enter a word to delete
-1

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Applications:

1. Index Pairs of a String:


Given a text string and words (a list of strings), return all index pairs [i, j] so that the
substring text[i]...text[j] is in the list of words.

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].

Java Program for Index Pairs of a String using Trie DS: IndexPairs.java

import java.util.*;

class Solution
{
public int[][] indexPairs(String text, String[] words)
{
/*initializing tire and put all word from words into Trie.*/
Trie trie=new Trie();
for(String s:words){
Trie cur=trie;
for(char c:s.toCharArray()){
if(cur.children[c-'a']==null){
cur.children[c-'a']=new Trie();
}
cur=cur.children[c-'a'];
}
cur.end=true; /*mark there is a word*/
}

/*if text is "ababa", check "ababa","baba","aba","ba","a" individually.*/


int len=text.length();
List<int[]> list=new ArrayList<>();
for(int i=0;i<len;i++){

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Trie cur=trie;
char cc=text.charAt(i);
int j=i; /*j is our moving index*/

while(cur.children[cc-'a']!=null){
cur=cur.children[cc-'a'];
if(cur.end){ /*there is a word ending here, put into our list*/
list.add(new int[]{i,j});
}
j++;
if(j==len){ /*reach the end of the text, we stop*/
break;
}
else{
cc=text.charAt(j);
}
}
}
/*put all the pairs from list into array*/
int size=list.size();
int[][] res=new int[size][2];
int i=0;
for(int[] r:list){
res[i]=r;
i++;
}
return res;
}
}
class Trie
{
Trie[] children;
boolean end; /*indicate whether there is a word*/
public Trie(){
end=false;
children=new Trie[26];
}
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

class IndexPairs
{
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
String org=sc.nextLine();
String dict[]=sc.nextLine().split(" ");
int res[][]=new Solution().indexPairs(org,dict);
for(int[] ans: res)
{
System.out.println(ans[0]+" "+ans[1]);
}
}
}

Sample Input-1:
---------------
thekmecandngitcolleges
colleges kmec ngit

Sample Output-1:
----------------
36
10 13
14 21

Sample Input-2:
---------------
xyxyx
xyx xy

Sample Output-2:
----------------
01
02
23
24

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

2. Longest word with all prefixes:

Given an array of strings words, find the longest string in words such that every prefix of it
is also in words.

 For example, let words = ["a", "app", "ap"].

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:

Input: words = [“k”,”ki”,”kir”,”kira”, “kiran”]

Output: “kiran”

Explanation: “kiran” has prefixes “kira”, “kir”, “ki”, and “k”, and all of them appear in
words.

Example 2:

Input: words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]

Output: “apple”

Explanation: Both “apple” and “apply” have all their prefixes in words. However, “apple” is
lexicographically smaller, so we return that.

Example 3:

Input: words = [“abc”, “bc”, “ab”, “qwe”]

Output: “ ”

Solution:

Use a trie to store all the words. Then do depth first search on the trie. Search the nodes from
left to right to make sure that the leftmost nodes (with lexicographically smallest letters) are
visited first. For the words that all nodes are ends of words, update the longest word. Finally,
return the longest word.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Java Program for Longest word with all prefixes using Trie DS: LongestWord.java

import java.util.*;

class Solution
{
Trie root = new Trie();
String res = "";
public String longestWord(String[] words)
{
for (String word : words) addWord(word);
for (String word : words) searchPrefix(word);
return res;
}

private void searchPrefix(String word)


. {
Trie cur = root;
for (char c : word.toCharArray())
{
cur = cur.children[c-'a'];
if (!cur.isWord) return;
}
if (res.length() < word.length() ||
res.length() == word.length() && res.compareTo(word) > 0) res = word;
}

private void addWord(String word)


{
Trie cur = root;
for(char c : word.toCharArray())
{
if (cur.children[c-'a'] == null) cur.children[c-'a'] = new Trie();
cur = cur.children[c-'a'];
}
cur.isWord = true;
}
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

class Trie
{
Trie[] children = new Trie[26];
boolean isWord;
}

class LongestWord
{
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
String dict[]=sc.nextLine().split(" ");
System.out.println(new Solution().longestWord(dict));
}
}

Sample Test case =1


input = k kmi km kmit kme kmec ksj ksjc ks kmecs
output = "kmecs"

Sample Test case =2


input = t tanker tup tupl tu tuple tupla
output = "tupla"

Sample Testcase =3
input = abc bc ab abcd
output = ""

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

3. Top K frequent words.

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:

In this method, we will use Trie data structure and bucket sort

Trie data structure is like a tree which is used to store strings. It is also known as the
digital tree or prefix tree. There can be maximum of 26 children in each node of the trie and
the root node is always the null node.

Bucket Sort algorithm is used for sorting elements by arranging them into multiple groups
which are known as buckets. These buckets are sorted by using any sorting algorithm and
then these sorted buckets are combined to form a complete sorted list.

Algorithm:
 We will use unordered map to store the frequency of each word.
 Since, in this question the least frequency of any word can be greater than or equal to
one and the maximum frequency is less than or equal to the length of the given vector
so we will use bucket sort to store words.
 Then for each bucket we will define a trie to store the words of the same frequency.
 We don't have to sort the words inside each bucket as the words will be arranged in
the alphabetical order inside trie.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Java Program for Top K frequent words:

import java.util.*;

class Node
{
public char c;
public boolean isWord;
public int count;
public Node[] children;
public String str;
public Node(char c)
{
this.c = c;
this.isWord = false;
this.count = 0;
children = new Node[26];
str = "";
}
}

class Trie
{
public Node root;
public Trie()
{
this.root = new Node('\0');
}
public void insert(String word)
{
Node curr = root;
for(int i = 0;i<word.length();i++)
{
char c = word.charAt(i);
if(curr.children[c-'a'] == null)
{
curr.children[c-'a'] = new Node(c);
}
curr = curr.children[c-'a'];
}
curr.isWord = true;
curr.count +=1;
curr.str = word;

}
public void traverse(Node root, PriorityQueue<Node> pq)
{
if(root.isWord == true)
pq.offer(root);

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

for(int i = 0;i<26;i++)
{
if(root.children[i]!=null)
traverse(root.children[i], pq);
}
}
}

public class FrequentWord


{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
String line1 = sc.nextLine();
int p = sc.nextInt();
String[] words = line1.split(",");
Trie t = new Trie();
PriorityQueue<Node> pq = new PriorityQueue<>(
(a,b)->{
if(a.count!=b.count)
return b.count-a.count;

return a.str.compareTo(b.str);
}
);

for(int i = 0;i<words.length;i++)
{
t.insert(words[i]);
}
t.traverse(t.root, pq);

List<String> res = new ArrayList<>();


int k = 0;
while(k++<p)
res.add(pq.poll().str);

System.out.println(res);
}
}
case =1
input =ball,are,case,doll,egg,case,doll,egg,are,are,egg,case,are,egg,are,case
3
output =[are, case, egg]

case =2
input =ball,are,case,doll,egg,case,doll,egg,are,are,egg,case,are,egg,are
3
output =[are, egg, case]

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

Suffix tree

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.

A suffix tree represents all the suffixes of a string in a tree-like data structure. The
root of the tree represents the empty string, and each leaf of the tree represents a suffix of the
string. Each internal node of the tree represents a common substring of two or more suffixes.

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 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.

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

The above is the sample suffixtree for the word banana

Java program to implement suffixtree:


import java.util.*;

class Suffix
{
static class SuffixTrieNode
{
static final int MAX_CHAR = 26;
SuffixTrieNode[] children = new SuffixTrieNode[MAX_CHAR];
SuffixTrieNode()
{
for (int i = 0; i < MAX_CHAR; i++)
children[i] = null;
}
void insertSuffix(String s)
{
System.out.println("substring " + s);
if (s.length() > 0)
{
char cIndex = (char) (s.charAt(0) - 'a');
if (children[cIndex] == null)
{
System.out.println("children null");
children[cIndex] = new SuffixTrieNode();
}
children[cIndex].insertSuffix(s.substring(1));
}
}

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

}
static class Suffix_trie
{
static final int MAX_CHAR = 26;
SuffixTrieNode root;
Suffix_trie(String s)
{
root = new SuffixTrieNode();
for (int i = 0; i < s.length(); i++)
root.insertSuffix(s.substring(i));
}
int _countNodesInTrie(SuffixTrieNode node)
{
if (node == null)
return 0;
int count = 0;
for (int i = 0; i < MAX_CHAR; i++)
{
if (node.children[i] != null)
count += _countNodesInTrie(node.children[i]);
}
return (1 + count);
}
int countNodesInTrie()
{
return _countNodesInTrie(root);
}
}

static int countDistinctSubstring(String str)


{
Suffix_trie sTrie = new Suffix_trie(str);
return sTrie.countNodesInTrie();
}
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
System.out.println("Enter any string to construct suffix tree");
String str=sc.nextLine();
System.out.println("Count of distinct substrings is "
+ (countDistinctSubstring(str) - 1));
}
}

Input :Enter any string to construct suffix tree


banana
output :
substring banana
children null
substring anana

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)


COMPETITIVE PROGRAMMING UNIT-III III –II SEM (KR21 Regulations)

children null
substring nana
children null
substring ana
children null
substring na
children null
substring a
children null
substring
substring anana
children null
substring nana
children null
substring ana
children null
substring na
children null
substring a
children null
substring
substring nana
children null
substring ana
children null
substring na
children null
substring a
children null
substring
substring ana
substring na
substring a
substring
substring na
substring a
substring
substring a
substring
Count of distinct substrings is 15

KESHAV MEMORIAL INSTITUTE OF TECHNOLOGY (AN AUTONOMOUS INSTITUTE)

You might also like