
- DSA - Home
- DSA - Overview
- DSA - Environment Setup
- DSA - Algorithms Basics
- DSA - Asymptotic Analysis
- Data Structures
- DSA - Data Structure Basics
- DSA - Data Structures and Types
- DSA - Array Data Structure
- DSA - Skip List Data Structure
- Linked Lists
- DSA - Linked List Data Structure
- DSA - Doubly Linked List Data Structure
- DSA - Circular Linked List Data Structure
- Stack & Queue
- DSA - Stack Data Structure
- DSA - Expression Parsing
- DSA - Queue Data Structure
- DSA - Circular Queue Data Structure
- DSA - Priority Queue Data Structure
- DSA - Deque Data Structure
- Searching Algorithms
- DSA - Searching Algorithms
- DSA - Linear Search Algorithm
- DSA - Binary Search Algorithm
- DSA - Interpolation Search
- DSA - Jump Search Algorithm
- DSA - Exponential Search
- DSA - Fibonacci Search
- DSA - Sublist Search
- DSA - Hash Table
- Sorting Algorithms
- DSA - Sorting Algorithms
- DSA - Bubble Sort Algorithm
- DSA - Insertion Sort Algorithm
- DSA - Selection Sort Algorithm
- DSA - Merge Sort Algorithm
- DSA - Shell Sort Algorithm
- DSA - Heap Sort Algorithm
- DSA - Bucket Sort Algorithm
- DSA - Counting Sort Algorithm
- DSA - Radix Sort Algorithm
- DSA - Quick Sort Algorithm
- Matrices Data Structure
- DSA - Matrices Data Structure
- DSA - Lup Decomposition In Matrices
- DSA - Lu Decomposition In Matrices
- Graph Data Structure
- DSA - Graph Data Structure
- DSA - Depth First Traversal
- DSA - Breadth First Traversal
- DSA - Spanning Tree
- DSA - Topological Sorting
- DSA - Strongly Connected Components
- DSA - Biconnected Components
- DSA - Augmenting Path
- DSA - Network Flow Problems
- DSA - Flow Networks In Data Structures
- DSA - Edmonds Blossom Algorithm
- DSA - Maxflow Mincut Theorem
- Tree Data Structure
- DSA - Tree Data Structure
- DSA - Tree Traversal
- DSA - Binary Search Tree
- DSA - AVL Tree
- DSA - Red Black Trees
- DSA - B Trees
- DSA - B+ Trees
- DSA - Splay Trees
- DSA - Range Queries
- DSA - Segment Trees
- DSA - Fenwick Tree
- DSA - Fusion Tree
- DSA - Hashed Array Tree
- DSA - K-Ary Tree
- DSA - Kd Trees
- DSA - Priority Search Tree Data Structure
- Recursion
- DSA - Recursion Algorithms
- DSA - Tower of Hanoi Using Recursion
- DSA - Fibonacci Series Using Recursion
- Divide and Conquer
- DSA - Divide and Conquer
- DSA - Max-Min Problem
- DSA - Strassen's Matrix Multiplication
- DSA - Karatsuba Algorithm
- Greedy Algorithms
- DSA - Greedy Algorithms
- DSA - Travelling Salesman Problem (Greedy Approach)
- DSA - Prim's Minimal Spanning Tree
- DSA - Kruskal's Minimal Spanning Tree
- DSA - Dijkstra's Shortest Path Algorithm
- DSA - Map Colouring Algorithm
- DSA - Fractional Knapsack Problem
- DSA - Job Sequencing with Deadline
- DSA - Optimal Merge Pattern Algorithm
- Dynamic Programming
- DSA - Dynamic Programming
- DSA - Matrix Chain Multiplication
- DSA - Floyd Warshall Algorithm
- DSA - 0-1 Knapsack Problem
- DSA - Longest Common Sub-sequence Algorithm
- DSA - Travelling Salesman Problem (Dynamic Approach)
- Hashing
- DSA - Hashing Data Structure
- DSA - Collision In Hashing
- Disjoint Set
- DSA - Disjoint Set
- DSA - Path Compression And Union By Rank
- Heap
- DSA - Heap Data Structure
- DSA - Binary Heap
- DSA - Binomial Heap
- DSA - Fibonacci Heap
- Tries Data Structure
- DSA - Tries
- DSA - Standard Tries
- DSA - Compressed Tries
- DSA - Suffix Tries
- Treaps
- DSA - Treaps Data Structure
- Bit Mask
- DSA - Bit Mask In Data Structures
- Bloom Filter
- DSA - Bloom Filter Data Structure
- Approximation Algorithms
- DSA - Approximation Algorithms
- DSA - Vertex Cover Algorithm
- DSA - Set Cover Problem
- DSA - Travelling Salesman Problem (Approximation Approach)
- Randomized Algorithms
- DSA - Randomized Algorithms
- DSA - Randomized Quick Sort Algorithm
- DSA - Karger’s Minimum Cut Algorithm
- DSA - Fisher-Yates Shuffle Algorithm
- Miscellaneous
- DSA - Infix to Postfix
- DSA - Bellmon Ford Shortest Path
- DSA - Maximum Bipartite Matching
- DSA Useful Resources
- DSA - Questions and Answers
- DSA - Selection Sort Interview Questions
- DSA - Merge Sort Interview Questions
- DSA - Insertion Sort Interview Questions
- DSA - Heap Sort Interview Questions
- DSA - Bubble Sort Interview Questions
- DSA - Bucket Sort Interview Questions
- DSA - Radix Sort Interview Questions
- DSA - Cycle Sort Interview Questions
- DSA - Quick Guide
- DSA - Useful Resources
- DSA - Discussion
Suffix Tries
A suffix trie is a type of trie mainly used for representing all the suffixes of a string. It is a tree where each node represents the suffix of a string. The root node is the entire string, and each child node is a suffix of the string that's one character shorter than the parent node's suffix.
For example, consider the string "banana". The suffix trie for this string would look like this:
root / | \ b a n / | \ a n a / | \ n a n / | \ a n a / | \ n a $ / | a $ / $
Each path from the root to a leaf node represents a suffix of the string. The leaf nodes represent the end of a suffix, and the dollar sign ($) is used to indicate the end of the string.
The children store entire word instead of characters.
Features of Suffix Tries
Following are the Features of Suffix Tries −
It is trie, so it stores suffixes of string in trie data structure. for string of length n, there are n suffixes.
We can say compact representation of all suffixes of a string. Means, to save space, it becomes Suffix Tree by collapsing the nodes with only one child.
Suffix tries are effective for sub-string searches, pattern matching, and other string-related operations.
Operations on Suffix Tries
Some of the common operations on suffix tries include:
- Insertion: Insert a new suffix into the trie.
- Deletion: Remove a suffix from the trie.
- Search: Find a specific suffix in the trie.
Insertion Operation on Suffix Tries
We can insert a new suffix into the trie by following these steps:
1. Start at the root node. 2. For each character in the suffix: a. If the character is not in the current node's children, create a new node for the same. b. Then move to the child node corresponding to the character and repeat the process. 3. Repeat the process for all character in the suffix. 4. Mark the last node as a leaf node to indicate the end of the suffix.
Code for Insertion Operation
For example, to insert the suffix "ana" into the trie, we would follow these steps:
// C Program to insert a suffix into a trie #include <stdio.h> #include <stdlib.h> #define ALPHABET_SIZE 26 // Structure for trie node struct TrieNode { struct TrieNode* children[ALPHABET_SIZE]; int isEndOfWord; }; // Function to create a new trie node struct TrieNode* createNode() { struct TrieNode* node = (struct TrieNode*)malloc(sizeof(struct TrieNode)); node->isEndOfWord = 0; for (int i = 0; i < ALPHABET_SIZE; i++) { node->children[i] = NULL; } return node; } // Function to insert a suffix into the trie void insert(struct TrieNode* root, const char* suffix) { struct TrieNode* current = root; for (int i = 0; suffix[i] != '\0'; i++) { int index = suffix[i] - 'a'; if (current->children[index] == NULL) { current->children[index] = createNode(); } current = current->children[index]; } current->isEndOfWord = 1; } // Display the trie void display(struct TrieNode* root, char str[], int level) { if (root->isEndOfWord) { str[level] = '\0'; printf("%s\n", str); } for (int i = 0; i < ALPHABET_SIZE; i++) { if (root->children[i] != NULL) { str[level] = i + 'a'; display(root->children[i], str, level + 1); } } } int main() { struct TrieNode* root = createNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); char str[20]; // Allocating memory for string buffer display(root, str, 0); return 0; }
Output
Following is the output of the above code:
a ana banana na
// C++ Program to insert a suffix into a trie #include <iostream> #include <vector> using namespace std; #define ALPHABET_SIZE 26 // Structure for trie node struct TrieNode { TrieNode* children[ALPHABET_SIZE]; bool isEndOfWord; }; // Function to create a new trie node TrieNode* createNode() { TrieNode* node = new TrieNode(); node->isEndOfWord = false; for (int i = 0; i < ALPHABET_SIZE; i++) { node->children[i] = NULL; } return node; } // Function to insert a suffix into the trie void insert(TrieNode* root, const string& suffix) { TrieNode* current = root; for (char c : suffix) { int index = c - 'a'; if (current->children[index] == NULL) { current->children[index] = createNode(); } current = current->children[index]; } current->isEndOfWord = true; } // Display the trie void display(TrieNode* root, char str[], int level) { if (root->isEndOfWord) { str[level] = '\0'; cout << str << endl; } for (int i = 0; i < ALPHABET_SIZE; i++) { if (root->children[i] != NULL) { str[level] = i + 'a'; display(root->children[i], str, level + 1); } } } int main() { TrieNode* root = createNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); char str[20]; // Allocating memory for string buffer display(root, str, 0); return 0; }
Output
Following is the output of the above code:
a ana banana na
// Java Program to insert a suffix into a trie class TrieNode { TrieNode[] children; boolean isEndOfWord; TrieNode() { children = new TrieNode[26]; // Only 'a' to 'z' isEndOfWord = false; } } public class Main { static final int ALPHABET_SIZE = 26; // Function to insert a suffix into the trie static void insert(TrieNode root, String suffix) { TrieNode current = root; for (int i = 0; i < suffix.length(); i++) { char ch = suffix.charAt(i); // Ignore non-alphabet characters if (ch < 'a' || ch > 'z') continue; int index = ch - 'a'; if (current.children[index] == null) { current.children[index] = new TrieNode(); } current = current.children[index]; } current.isEndOfWord = true; } // Display the trie static void display(TrieNode root, char[] str, int level) { if (root.isEndOfWord) { System.out.println(new String(str, 0, level)); // Correct substring printing } for (int i = 0; i < ALPHABET_SIZE; i++) { if (root.children[i] != null) { str[level] = (char) (i + 'a'); display(root.children[i], str, level + 1); } } } public static void main(String[] args) { TrieNode root = new TrieNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); // '$' will be ignored safely char[] str = new char[20]; // Allocating memory for string buffer display(root, str, 0); } }
Output
Following is the output of the above code:
a ana banana na
# Python Program to insert a suffix into a trie # Python Program to insert a suffix into a trie class TrieNode: def __init__(self): self.children = [None] * 26 # Only for 'a' to 'z' self.isEndOfWord = False # Function to insert a suffix into the trie def insert(root, suffix): current = root for c in suffix: # Ignore non-alphabet characters if not ('a' <= c <= 'z'): continue # Skip invalid characters index = ord(c) - ord('a') if current.children[index] is None: current.children[index] = TrieNode() current = current.children[index] current.isEndOfWord = True # Display the trie def display(root, str, level): if root.isEndOfWord: print(''.join(str[:level])) # Print valid words for i in range(26): if root.children[i] is not None: str[level] = chr(i + ord('a')) display(root.children[i], str, level + 1) if __name__ == '__main__': root = TrieNode() insert(root, "banana") insert(root, "ana") insert(root, "na") insert(root, "a") insert(root, "$") # '$' will be ignored safely str = [''] * 20 # Allocating memory for string buffer display(root, str, 0)
Output
Following is the output of the above code:
a ana banana na
Deletion and Search Operations on Suffix Tries
Deletion and search operations on suffix tries are similar to insertion. We can delete a suffix by removing the corresponding leaf node from the trie. To search for a specific suffix, we can traverse the trie from the root node to the leaf node corresponding to the suffix.
Following are the steps to delete a suffix from the trie:
1. Start at the root node. 2. For each character in the suffix: a. Move to the child node corresponding to the character. 3. Mark the last node as a non-leaf node to delete the suffix.
Following are the steps to search for a specific suffix in the trie:
1. Start at the root node. 2. For each character in the suffix: a. Move to the child node corresponding to the character. 3. Check if the last node is a leaf node to find the suffix.
Code for Deletion and Search Operations
Here is the code for deletion and search operations on suffix tries:
// C Program to delete and search a suffix in a trie #include <stdio.h> #include <stdlib.h> #define ALPHABET_SIZE 26 // Structure for trie node struct TrieNode { struct TrieNode* children[ALPHABET_SIZE]; int isEndOfWord; }; // Function to create a new trie node struct TrieNode* createNode() { struct TrieNode* node = (struct TrieNode*)malloc(sizeof(struct TrieNode)); node->isEndOfWord = 0; for (int i = 0; i < ALPHABET_SIZE; i++) { node->children[i] = NULL; } return node; } // Function to insert a suffix into the trie void insert(struct TrieNode* root, const char* suffix) { struct TrieNode* current = root; for (int i = 0; suffix[i] != '\0'; i++) { int index = suffix[i] - 'a'; if (current->children[index] == NULL) { current->children[index] = createNode(); } current = current->children[index]; } current->isEndOfWord = 1; } // Function to delete a suffix from the trie void delete(struct TrieNode* root, const char* suffix) { struct TrieNode* current = root; for (int i = 0; suffix[i] != '\0'; i++) { int index = suffix[i] - 'a'; if (current->children[index] == NULL) { return; // Suffix not found } current = current->children[index]; } current->isEndOfWord = 0; } // Function to search for a suffix in the trie int search(struct TrieNode* root, const char* suffix) { struct TrieNode* current = root; for (int i = 0; suffix[i] != '\0'; i++) { int index = suffix[i] - 'a'; if (current->children[index] == NULL) { return 0; // Suffix not found } current = current->children[index]; } return current->isEndOfWord; } int main() { struct TrieNode* root = createNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); printf("Search for 'ana': %s\n", search(root, "ana") ? "Found" : "Not found"); delete(root, "ana"); printf("After deletion:\n"); printf("Search for 'ana': %s\n", search(root, "ana") ? "Found" : "Not found"); printf("Search for 'na': %s\n", search(root, "na") ? "Found" : "Not found"); return 0; }
Output
Following is the output of the above code:
Search for 'ana': Found After deletion: Search for 'ana': Not found Search for 'na': Found
// C++ Program to delete and search a suffix in a trie #include <iostream> #include <vector> using namespace std; #define ALPHABET_SIZE 26 // Structure for trie node struct TrieNode { TrieNode* children[ALPHABET_SIZE]; bool isEndOfWord; }; // Function to create a new trie node TrieNode* createNode() { TrieNode* node = new TrieNode(); node->isEndOfWord = false; for (int i = 0; i < ALPHABET_SIZE; i++) { node->children[i] = NULL; } return node; } // Function to insert a suffix into the trie void insert(TrieNode* root, const string& suffix) { TrieNode* current = root; for (char c : suffix) { int index = c - 'a'; if (current->children[index] == NULL) { current->children[index] = createNode(); } current = current->children[index]; } current->isEndOfWord = true; } // Function to delete a suffix from the trie void deleteN(TrieNode* root, const string& suffix) { TrieNode* current = root; for (char c : suffix) { int index = c - 'a'; if (current->children[index] == NULL) { return; // Suffix not found } current = current->children[index]; } current->isEndOfWord = false; } // Function to search for a suffix in the trie bool search(TrieNode* root, const string& suffix) { TrieNode* current = root; for (char c : suffix) { int index = c - 'a'; if (current->children[index] == NULL) { return false; // Suffix not found } current = current->children[index]; } return current->isEndOfWord; } int main() { TrieNode* root = createNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); cout << "Search for 'ana': " << (search(root, "ana") ? "Found" : "Not found") << endl; deleteN(root, "ana"); cout << "After deletion:\n"; cout << "Search for 'ana': " << (search(root, "ana") ? "Found" : "Not found") << endl; cout << "Search for 'na': " << (search(root, "na") ? "Found" : "Not found") << endl; return 0; }
Output
Following is the output of the above code:
Search for 'ana': Found After deletion: Search for 'ana': Not found Search for 'na': Found
// Java Program to delete and search a suffix in a trie class TrieNode { TrieNode[] children; boolean isEndOfWord; TrieNode() { children = new TrieNode[26]; // Only 'a' to 'z' isEndOfWord = false; } } public class Main { static final int ALPHABET_SIZE = 26; // Function to insert a suffix into the trie static void insert(TrieNode root, String suffix) { TrieNode current = root; for (int i = 0; i < suffix.length(); i++) { char ch = suffix.charAt(i); // Ignore non-alphabet characters if (ch < 'a' || ch > 'z') continue; int index = ch - 'a'; if (current.children[index] == null) { current.children[index] = new TrieNode(); } current = current.children[index]; } current.isEndOfWord = true; } // Function to delete a suffix from the trie static void delete(TrieNode root, String suffix) { TrieNode current = root; for (int i = 0; i < suffix.length(); i++) { char ch = suffix.charAt(i); // Ignore non-alphabet characters if (ch < 'a' || ch > 'z') return; int index = ch - 'a'; if (current.children[index] == null) { return; // Suffix not found } current = current.children[index]; } current.isEndOfWord = false; } // Function to search for a suffix in the trie static boolean search(TrieNode root, String suffix) { TrieNode current = root; for (int i = 0; i < suffix.length(); i++) { char ch = suffix.charAt(i); // Ignore non-alphabet characters if (ch < 'a' || ch > 'z') return false; int index = ch - 'a'; if (current.children[index] == null) { return false; // Suffix not found } current = current.children[index]; } return current.isEndOfWord; } public static void main(String[] args) { TrieNode root = new TrieNode(); insert(root, "banana"); insert(root, "ana"); insert(root, "na"); insert(root, "a"); insert(root, "$"); System.out.println("Search for 'ana': " + (search(root, "ana") ? "Found" : "Not found")); delete(root, "ana"); System.out.println("After deletion:"); System.out.println("Search for 'ana': " + (search(root, "ana") ? "Found" : "Not found")); System.out.println("Search for 'na': " + (search(root, "na") ? "Found" : "Not found")); } }
Output
Following is the output of the above code:
Search for 'ana': Found After deletion: Search for 'ana': Not found Search for 'na': Found
# Python Program to delete and search a suffix in a trie class TrieNode: def __init__(self): self.children = [None] * 26 # Only for 'a' to 'z' self.isEndOfWord = False # Function to insert a suffix into the trie def insert(root, suffix): current = root for c in suffix: # Ignore non-alphabet characters if not ('a' <= c <= 'z'): continue # Skip invalid characters index = ord(c) - ord('a') if current.children[index] is None: current.children[index] = TrieNode() current = current.children[index] current.isEndOfWord = True # Function to delete a suffix from the trie def delete(root, suffix): current = root for c in suffix: # Ignore non-alphabet characters if not ('a' <= c <= 'z'): return # Skip invalid characters index = ord(c) - ord('a') if current.children[index] is None: return # Suffix not found current = current.children[index] current.isEndOfWord = False # Function to search for a suffix in the trie def search(root, suffix): current = root for c in suffix: # Ignore non-alphabet characters if not ('a' <= c <= 'z'): return False # Skip invalid characters index = ord(c) - ord('a') if current.children[index] is None: return False # Suffix not found current = current.children[index] return current.isEndOfWord if __name__ == '__main__': root = TrieNode() insert(root, "banana") insert(root, "ana") insert(root, "na") insert(root, "a") insert(root, "$") print("Search for 'ana':", "Found" if search(root, "ana") else "Not found") delete(root, "ana") print("After deletion:") print("Search for 'ana':", "Found" if search(root, "ana") else "Not found") print("Search for 'na':", "Found" if search(root, "na") else "Not found")
Output
Following is the output of the above code:
Search for 'ana': Found After deletion: Search for 'ana': Not found Search for 'na': Found
Time Complexity of Suffix Tries
The time complexity of suffix tries depends on the operations performed:
- Insertion: O(n) for inserting a suffix of length n into the trie.
- Deletion: O(n)
- Search: O(n)
Applications of Suffix Tries
Suffix tries are used in various applications, including:
- Pattern matching and substring search algorithms
- Text indexing and searching
- Genomic sequence analysis
- Spell checking and correction
- Biological sequence analysis
- Information retrieval
- Compression algorithms
Conclusion
In this chapter, we discussed suffix tries, a type of trie used for representing all the suffixes of a string. We explored the structure of a suffix trie, its features, operations, and applications. Suffix tries are a powerful data structure for substring searches, pattern matching, and other string-related operations.