Find strings that end with a given suffix

Given a set of strings S and a string P, the task is to print all strings from the set with suffix P.

Examples:

Input:
S = {“geeks”, “geeksforgeeks”, “geek”, “newgeeks”, “friendsongeeks”, “toppergeek”}
P = “geeks”
Output:
geeks
friendsongeeks
geeksforgeeks
newgeeks

Input:
S = {“wideworld”, “webworld”, “classicword”, “world”, “worldclass”}
P = “world”
Output:
wideworld
webworld
world

Approach: The idea is to use Pattern Searching using a Trie of all Suffixes.



  • Store the strings in a Trie in reverse order.
  • Reverse the string P and search for it using standard Trie search algorithm.
  • Check if the reversed string P is itself a word in Trie, which be checked by seeing if the last matching node has isEndWord flag set.
  • Otherwise if the reversed string P forms a suffix, then recursively print all nodes under the subtree of last matching node.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ code to print all
// strings from a given set
// with suffix P
#include <bits/stdc++.h>
using namespace std;
  
#define CHILD (26)
  
// Converts key current character
// into index use only 'a' through
// 'z' and lower case
#define CHAR_TO_INDEX(c) (int)(c - 'a')
  
// Trie node
struct TrieNode {
    struct TrieNode* children[CHILD];
  
    // isWordEnd is true if the node
    // represents end of a word
    bool isWordEnd;
};
  
// Function to reverse a string
string reverseStr(string str)
{
    int n = str.length();
  
    // Swap character starting from
    // two corners
    for (int i = 0; i < n / 2; i++)
        swap(str[i], str[n - i - 1]);
    return str;
}
  
// Returns new trie node
struct TrieNode* getNode(void)
{
    struct TrieNode* pNode = new TrieNode;
    pNode->isWordEnd = false;
  
    for (int i = 0; i < CHILD; i++)
        pNode->children[i] = NULL;
  
    return pNode;
}
  
// If not present, inserts key into
// trie. If the key is suffix of trie
// node, just mark leaf node
void insert(struct TrieNode* root,
            const string key)
{
    struct TrieNode* pCrawl = root;
  
    for (int level = 0;
         level < key.length();
         level++) {
  
        int index = CHAR_TO_INDEX(key[level]);
  
        if (!pCrawl->children[index])
            pCrawl->children[index] = getNode();
  
        pCrawl = pCrawl->children[index];
    }
  
    // Mark last node as leaf
    pCrawl->isWordEnd = true;
}
  
// Returns true if key presents in
// the trie, else false
bool search(struct TrieNode* root,
            const string key)
{
    int length = key.length();
    struct TrieNode* pCrawl = root;
    for (int level = 0;
         level < length; level++) {
  
        int index = CHAR_TO_INDEX(key[level]);
  
        if (!pCrawl->children[index])
            return false;
  
        pCrawl = pCrawl->children[index];
    }
  
    return (pCrawl != NULL
            && pCrawl->isWordEnd);
}
  
// Returns 0 if current node has
// a child
// If all children are NULL, return 1
bool isLastNode(struct TrieNode* root)
{
    for (int i = 0; i < CHILD; i++)
        if (root->children[i])
            return 0;
    return 1;
}
  
// Recursive function to print strings
// having given suffix
void printStrings(struct TrieNode* root,
                  string currsuffix)
{
  
    // If a string with currsuffix
    // is found
    if (root->isWordEnd) {
        cout << reverseStr(currsuffix);
        cout << endl;
        reverseStr(currsuffix);
    }
  
    // All children struct node
    // pointers are NULL
    if (isLastNode(root))
        return;
  
    for (int i = 0; i < CHILD; i++) {
        if (root->children[i]) {
  
            // Append current character
            // to currsuffix string
            currsuffix.push_back(97 + i);
  
            // recur over the rest
            printStrings(root->children[i],
                         currsuffix);
            // remove last character
            currsuffix.pop_back();
        }
    }
}
  
// print strings with given suffix
int printStringsWithGivenSuffix(
    TrieNode* root, const string query)
{
    struct TrieNode* pCrawl = root;
  
    // Check if suffix is present
    // and find the node (of last
    // level) with last character
    // of given string.
    int level;
    int n = query.length();
    for (level = 0; level < n;
         level++) {
  
        int index = CHAR_TO_INDEX(query[level]);
  
        // no string in the Trie has
        // this suffix
        if (!pCrawl->children[index])
            return 0;
  
        pCrawl = pCrawl->children[index];
    }
  
    // If suffix is present as a word.
    bool isWord = (pCrawl->isWordEnd
                   == true);
  
    // If suffix is last node of
    // tree (has no children)
    bool isLast = isLastNode(pCrawl);
  
    // If suffix is present as a word,
    // but there is no subtree below
    // the last matching node.
    if (isWord && isLast) {
        cout << query << endl;
        return -1;
    }
  
    // If there are are nodes below
    // last matching character.
    if (!isLast) {
        string suffix = query;
        printStrings(pCrawl, suffix);
        return 1;
    }
}
  
// Driver Code
int main()
{
    struct TrieNode* root = getNode();
    vector<string> S
        = { "geeks", "geeksforgeeks",
            "geek", "newgeeks",
            "friendsongeeks",
            "toppergeek" };
  
    for (string str : S) {
        insert(root,
               reverseStr(str));
    }
  
    string P = "eek";
  
    printStringsWithGivenSuffix(
        root, reverseStr(P));
  
    return 0;
}

chevron_right


Python

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python3 code for the above program
class TrieNode(): 
    def __init__(self): 
          
        # Initialize one node for trie 
        self.children = {} 
        self.last = False
  
def reverse(s): 
    str = "" 
    for i in s: 
        str = i + str
    return str
  
class Trie(): 
    def __init__(self): 
          
        # Initialize the trie structure
        self.root = TrieNode() 
        self.word_list = [] 
  
    def formTrie(self, keys): 
          
        # Forms a trie structure
        # with the given set of 
        # strings if it does not
        # exists already else it 
        # merges the key into it
        # by extending the 
        # structure as required 
        for key in keys:
              
            # inserting one key
            # to the trie.
            self.insert(key)  
  
    def insert(self, key): 
          
        # Inserts a key into 
        # trie if it does not 
        # exist already. And if 
        # the key is a suffix
        # of the trie node, just 
        # marks it as leaf node. 
        node = self.root 
  
        for a in list(key): 
            if not node.children.get(a): 
                node.children[a] = TrieNode() 
  
            node = node.children[a] 
  
        node.last = True
  
    def search(self, key): 
          
        # Searches the given key
        # in trie for a full match 
        # and returns True on
        # success else returns False 
        node = self.root 
        found = True
  
        for a in list(key): 
            if not node.children.get(a): 
                found = False
                break
  
            node = node.children[a] 
  
        return node and node.last and found 
  
    def printStrings(self, node, word): 
          
        # Method to recursively
        # traverse the trie 
        # and return a whole word
        if node.last: 
            self.word_list.append(word) 
  
        for a, n in node.children.items(): 
            self.printStrings(n, word + a) 
  
    def printStringsWithGivenSuffix(self, key): 
          
        # Returns all the words in 
        # the trie whose common 
        # suffix is the given key
        # thus listing out all 
        # the strings
        node = self.root 
        not_found = False
        temp_word = '' 
  
        for a in list(key): 
            if not node.children.get(a): 
                not_found = True
                break
  
            temp_word +=
            node = node.children[a] 
  
        if not_found: 
            return 0
        elif node.last and not node.children: 
            return -1
  
        self.printStrings(node, temp_word) 
  
        for s in self.word_list: 
            print(reverse(s)) 
        return 1
  
# Driver Code 
  
# keys to form the trie structure
keys = [reverse("geeks"), 
reverse("geeksforgeeks"), 
reverse("geek"), 
reverse("newgeeks"), 
reverse("friendsongeeks"), 
reverse("toppergeek")] 
  
# key 
key = "eek" 
status = ["Not found", "Found"
  
# creating trie object 
t = Trie() 
  
# creating the trie structure 
# with the given set of strings
t.formTrie(keys) 
  
# print string having suffix 'P'
# our trie structure
comp = t.printStringsWithGivenSuffix(reverse(key))   

chevron_right


Output:

geek
toppergeek

s

Don’t stop now and take your learning to the next level. Learn all the important concepts of Data Structures and Algorithms with the help of the most trusted course: DSA Self Paced. Become industry ready at a student-friendly price.




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.