Open In App

Sum of all LCP of maximum length by selecting any two Strings at a time

Improve
Improve
Like Article
Like
Save
Share
Report

Given a list of strings, the task is to find the sum of all LCP (Longest Common Prefix) of maximum length by selecting any two strings at a time. 
Examples: 
 

Input: str[] = {babab, ababb, abbab, aaaaa, babaa, babbb} 
Output:
Explanation: 
Choose 1st and 5th string => length of LCP = 4, 
Choose 2nd and 3rd string => length of LCP = 2 
Sum of LCP = 4 + 2 = 6 
 

Input: str = [“aa”, “aaaa”, “aaaaaaaa”, “aaaabaaaa”, “aaabaaa”] 
Output:
Explanation: 
Choose 3rd (aaaaaaaa) and 4th string (aaaabaaaa) => length of LCP (aaaa) = 4, 
Choose 2nd (aaaa) and 5th (aaabaaa) string => length of LCP (aaa) = 3 
Sum of LCP = 4 + 3 = 7 
 

 

Naive Approach: 
 

  • Sort the list of strings in decreasing order of their length
  • Then take the first string from the list and find the Longest Common Prefix with all other remaining string in the list and store it in the array
  • Choose the maximum value from the array and add it to variable answer and remove the pair of string from the list corresponding to that sum
  • Repeat the above procedures for all the next strings till the list is empty or you reach the last string
  • The variable answer has the required sum of all LCP of maximum length

Time Complexity: O(M*N2), where M = maximum string length and N = number of strings.
Efficient Approach: 
An efficient solution can be obtained using a Trie Data Structure. To find the number of characters common between the strings we will use the variable ‘visited’ to keep track of how many times one character is visited. 
Following are the steps: 
 

  • Insert list of string in trie such that every string in the list is inserted as an individual trie node. 
     
  • For all prefixes of maximum length, count the pairs from deepest node in the trie. 
     
  • Use depth-first search (DFS) traversal on trie to count the pairs from deepest node. 
     
  • If the value of visited node is more than one, it means that there two or more strings that have common prefix up till that node. 
     
  • Add the value of that visited node to a variable count. 
     
  • Decrease the value of that visited node from current and previous nodes such that the pair of words chosen for calculation must be removed. 
     
  • Repeat the above steps for all nodes and return the value of count. 
     

Below is the implementation of the above approach: 
 

C++




// C++ program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
 
#include <bits/stdc++.h>
using namespace std;
 
class TrieNode {
public:
    char val;
 
    // Using map to store the pointers
    // of children nodes for dynamic
    // implementation, for making the
    // program space efficient
    map<char, TrieNode*> children;
 
    // Counts the number of times the node
    // is visited while making the trie
    int visited;
 
    // Initially visited value for all
    // nodes is zero
    TrieNode(char x)
    {
        val = x;
        visited = 0;
    }
};
 
class Trie {
public:
    TrieNode* head;
 
    // Head node of the trie is initialize
    // as '\0', after this all strings add
    Trie()
    {
        head = new TrieNode('\0');
    }
 
    // Function to insert the strings in
    // the trie
    void addWord(string s)
    {
        TrieNode* temp = head;
        const unsigned int n = s.size();
 
        for (int i = 0; i < n; i++) {
 
            // Inserting character-by-character
            char ch = s[i];
 
            // If the node of ch is not present in
            // map make a new node and add in map
            if (!temp->children[ch]) {
                temp->children[ch] = new TrieNode(ch);
            }
            temp = temp->children[ch];
            temp->visited++;
        }
    }
 
    // Recursive function to calculate the
    // answer argument is passed by reference
    int dfs(TrieNode* node, int& ans, int depth)
    {
        // To store changed visited values from
        // children of this node i.e. number of
        // nodes visited by its children
        int vis = 0;
        for (auto child : node->children) {
            vis += dfs(child.second, ans, depth + 1);
        }
 
        // Updating the visited variable, telling
        // number of nodes that have
        // already been visited by its children
        node->visited -= vis;
        int string_pair = 0;
 
        // If node->visited > 1, means more than
        // one string has prefix up till this node
        // common in them
        if (node->visited > 1) {
 
            // Number of string pair with current
            // node common in them
            string_pair = (node->visited / 2);
            ans += (depth * string_pair);
 
            // Updating visited variable of current node
            node->visited -= (2 * string_pair);
        }
 
        // Returning the total number of nodes
        // already visited that needs to be
        // updated to previous node
        return (2 * string_pair + vis);
    }
 
    // Function to run the dfs function for the
    // first time and give the answer variable
    int dfshelper()
    {
 
        // Stores the final answer
        // as sum of all depths
        int ans = 0;
        dfs(head, ans, 0);
        return ans;
    }
};
 
// Driver Function
int main()
{
    Trie T;
    string str[]
        = { "babab", "ababb", "abbab",
            "aaaaa", "babaa", "babbb" };
 
    int n = 6;
    for (int i = 0; i < n; i++) {
        T.addWord(str[i]);
    }
    int ans = T.dfshelper();
    cout << ans << endl;
 
    return 0;
}


Java




// Java program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
import java.util.*;
 
class GFG
{
 
static class TrieNode
{
    char val;
 
    // Using map to store the pointers
    // of children nodes for dynamic
    // implementation, for making the
    // program space efficient
    HashMap<Character, TrieNode> children;
 
    // Counts the number of times the node
    // is visited while making the trie
    int visited;
 
    // Initially visited value for all
    // nodes is zero
    TrieNode(char x)
    {
        val = x;
        visited = 0;
        children = new HashMap<>();
    }
}
 
static class Trie
{
 
    TrieNode head;
    int ans;
 
    // Head node of the trie is initialize
    // as '\0', after this all Strings add
    Trie()
    {
        head = new TrieNode('\0');
        ans = 0;
    }
 
    // Function to insert the Strings in
    // the trie
    void addWord(String s)
    {
        TrieNode temp = head;
        int n = s.length();
 
        for (int i = 0; i < n; i++)
        {
 
            // Inserting character-by-character
            char ch = s.charAt(i);
 
            // If the node of ch is not present in
            // map make a new node and add in map
            if (temp.children.get(ch) == null)
            {
                temp.children.put(ch, new TrieNode(ch));
            }
            temp = temp.children.get(ch);
            temp.visited++;
        }
    }
 
    // Recursive function to calculate the
    // answer argument is passed by reference
    int dfs(TrieNode node, int depth)
    {
        // To store changed visited values from
        // children of this node i.e. number of
        // nodes visited by its children
        int vis = 0;
        Iterator hmIterator = node.children.entrySet().iterator();
        while (hmIterator.hasNext())
        {
            Map.Entry child = (Map.Entry)hmIterator.next();
            vis += dfs((TrieNode)child.getValue(), depth + 1);
        }
 
        // Updating the visited variable, telling
        // number of nodes that have
        // already been visited by its children
        node.visited -= vis;
        int String_pair = 0;
 
        // If node.visited > 1, means more than
        // one String has prefix up till this node
        // common in them
        if (node.visited > 1)
        {
 
            // Number of String pair with current
            // node common in them
            String_pair = (node.visited / 2);
            ans += (depth * String_pair);
 
            // Updating visited variable of current node
            node.visited -= (2 * String_pair);
        }
 
        // Returning the total number of nodes
        // already visited that needs to be
        // updated to previous node
        return (2 * String_pair + vis);
    }
 
    // Function to run the dfs function for the
    // first time and give the answer variable
    int dfshelper()
    {
 
        // Stores the final answer
        // as sum of all depths
        ans = 0;
        dfs(head, 0);
        return ans;
    }
}
 
// Driver code
public static void main(String args[])
{
    Trie T = new Trie();
    String str[]
        = { "babab", "ababb", "abbab",
            "aaaaa", "babaa", "babbb" };
 
    int n = 6;
    for (int i = 0; i < n; i++)
    {
        T.addWord(str[i]);
    }
    int ans = T.dfshelper();
    System.out.println( ans );
}
}
// This code is contributed by Arnab Kundu


Python




# python program to find Sum of all LCP
# of maximum length by selecting
# any two Strings at a time
class TrieNode:
    # Using map to store the pointers
    # of children nodes for dynamic
    # implementation, for making the
    # program space efficient
    def __init__(self, x):
        self.val = x
        self.children = {}
        # Counts the number of times the node
        # is visited while making the trie
        self.visited = 0
 
class Trie:
    def __init__(self):
        # Head node of the trie is initialize
        # as '\0', after this all strings add
        self.head = TrieNode('')
     
    # Function to insert the strings in
    # the trie
    def addWord(self, s):
        temp = self.head
        n = len(s)
 
        for i in range(n):
            ch = s[i]
            if ch not in temp.children:
                  # If the node of ch is not present in
                # map make a new node and add in map
                temp.children[ch] = TrieNode(ch)
            temp = temp.children[ch]
            temp.visited += 1
     
    # Recursive function to calculate the
    # answer argument is passed by reference
    def dfs(self, node, ans, depth):
          # To store changed visited values from
        # children of this node i.e. number of
        # nodes visited by its children
        vis = 0
        for child in node.children.values():
            vis += self.dfs(child, ans, depth + 1)
         
        # Updating the visited variable, telling
        # number of nodes that have
        # already been visited by its children
        node.visited -= vis
        string_pair = 0
        # If node->visited > 1, means more than
        # one string has prefix up till this node
        # common in them
        if node.visited > 1:
              # Number of string pair with current
            # node common in them
            string_pair = node.visited // 2
            ans[0] += (depth * string_pair)
            node.visited -= (2 * string_pair)
         
        # Returning the total number of nodes
        # already visited that needs to be
        # updated to previous node
        return 2 * string_pair + vis
     
    # Function to run the dfs function for the
    # first time and give the answer variable
    def dfshelper(self):
          # Stores the final answer
        # as sum of all depths
        ans = [0]
        self.dfs(self.head, ans, 0)
        return ans[0]
 
# Driver code
T = Trie()
str_list = ["babab", "ababb", "abbab", "aaaaa", "babaa", "babbb"]
for s in str_list:
    T.addWord(s)
ans = T.dfshelper()
print(ans)
 
# This code is contributed by bhardwajji


C#




// C# program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
using System;
using System.Collections.Generic;
 
class Program
{
    class TrieNode
    {
        public char val;
 
        // Using dictionary to store the pointers
        // of children nodes for dynamic
        // implementation, for making the
        // program space efficient
        public Dictionary<char, TrieNode> children;
 
        // Counts the number of times the node
        // is visited while making the trie
        public int visited;
 
        // Initially visited value for all
        // nodes is zero
        public TrieNode(char x)
        {
            val = x;
            visited = 0;
            children = new Dictionary<char, TrieNode>();
        }
    }
 
    class Trie
    {
        TrieNode head;
        int ans;
 
        // Head node of the trie is initialize
        // as '\0', after this all Strings add
        public Trie()
        {
            head = new TrieNode('\0');
            ans = 0;
        }
 
        // Function to insert the Strings in
        // the trie
        public void addWord(string s)
        {
            TrieNode temp = head;
            int n = s.Length;
 
            for (int i = 0; i < n; i++)
            {
                // Inserting character-by-character
                char ch = s[i];
 
                // If the node of ch is not present in
                // dictionary make a new node and add in dictionary
                if (!temp.children.ContainsKey(ch))
                {
                    temp.children.Add(ch, new TrieNode(ch));
                }
                temp = temp.children[ch];
                temp.visited++;
            }
        }
 
        // Recursive function to calculate the
        // answer argument is passed by reference
        int dfs(TrieNode node, int depth)
        {
            // To store changed visited values from
            // children of this node i.e. number of
            // nodes visited by its children
            int vis = 0;
            foreach (var child in node.children)
            {
                vis += dfs(child.Value, depth + 1);
            }
 
            // Updating the visited variable, telling
            // number of nodes that have
            // already been visited by its children
            node.visited -= vis;
            int String_pair = 0;
 
            // If node.visited > 1, means more than
            // one String has prefix up till this node
            // common in them
            if (node.visited > 1)
            {
                // Number of String pair with current
                // node common in them
                String_pair = (node.visited / 2);
                ans += (depth * String_pair);
 
                // Updating visited variable of current node
                node.visited -= (2 * String_pair);
            }
 
            // Returning the total number of nodes
            // already visited that needs to be
            // updated to previous node
            return (2 * String_pair + vis);
        }
 
        // Function to run the dfs function for the
        // first time and give the answer variable
        public int dfshelper()
        {
            // Stores the final answer
            // as sum of all depths
            ans = 0;
            dfs(head, 0);
            return ans;
    }
}
  
// Driver code
public static void Main()
{
    Trie T = new Trie();
    string[] str
        = { "babab", "ababb", "abbab",
            "aaaaa", "babaa", "babbb" };
  
    int n = 6;
    for (int i = 0; i < n; i++)
    {
        T.addWord(str[i]);
    }
    int ans = T.dfshelper();
    Console.WriteLine(ans);
}
}
 
// This code is contributed by Aman Kumar.


Javascript




<script>
// Javascript program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
 
class TrieNode
{
    // Initially visited value for all
    // nodes is zero
    constructor(x)
    {
        this.val = x;
         
        // Counts the number of times the node
    // is visited while making the trie
        this.visited = 0;
         
        // Using map to store the pointers
    // of children nodes for dynamic
    // implementation, for making the
    // program space efficient
        this.children = new Map();
    }
}
 
class Trie
{
    // Head node of the trie is initialize
    // as '\0', after this all Strings add
    constructor()
    {
        this.head = new TrieNode('\0');
        this.ans = 0;
    }
     
    // Function to insert the Strings in
    // the trie
    addWord(s)
    {
        let temp = this.head;
        let n = s.length;
   
        for (let i = 0; i < n; i++)
        {
   
            // Inserting character-by-character
            let ch = s[i];
   
            // If the node of ch is not present in
            // map make a new node and add in map
            if (temp.children.get(ch) == null)
            {
                temp.children.set(ch, new TrieNode(ch));
            }
            temp = temp.children.get(ch);
            temp.visited++;
        }
    }
     
    // Recursive function to calculate the
    // answer argument is passed by reference
    dfs(node,depth)
    {
        // To store changed visited values from
        // children of this node i.e. number of
        // nodes visited by its children
        let vis = 0;
         
        for(let [key, value] of node.children.entries())
        {
              
            vis += this.dfs(value, depth + 1);
        }
   
        // Updating the visited variable, telling
        // number of nodes that have
        // already been visited by its children
        node.visited -= vis;
        let String_pair = 0;
   
        // If node.visited > 1, means more than
        // one String has prefix up till this node
        // common in them
        if (node.visited > 1)
        {
   
            // Number of String pair with current
            // node common in them
            String_pair = (node.visited / 2);
            this.ans += (depth * String_pair);
   
            // Updating visited variable of current node
            node.visited -= (2 * String_pair);
        }
   
        // Returning the total number of nodes
        // already visited that needs to be
        // updated to previous node
        return (2 * String_pair + vis);
    }
     
    // Function to run the dfs function for the
    // first time and give the answer variable
    dfshelper()
    {
        // Stores the final answer
        // as sum of all depths
        this.ans = 0;
        this.dfs(this.head, 0);
        return this.ans;
    }
}
 
// Driver code
let T = new Trie();
let str
= [ "babab", "ababb", "abbab",
   "aaaaa", "babaa", "babbb" ];
 
let n = 6;
for (let i = 0; i < n; i++)
{
    T.addWord(str[i]);
}
let ans = T.dfshelper();
document.write( ans );
 
// This code is contributed by unknown2108
</script>


Output: 

6

 

Time Complexity: 
For inserting all the strings in the trie: O(MN) 
For performing trie traversal: O(26*M) ~ O(M) 
Therefore, overall Time complexity: O(M*N), where: 
 

N = Number of strings
M = Length of the largest string

Auxiliary Space: O(M)
 



Last Updated : 22 Feb, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads