Open In App

Find alphabetical order such that words can be considered sorted

Given an array of words, find any alphabetical order in the English alphabet such that the given words can be considered sorted (increasing), if there exists such an order, otherwise output impossible. 

Examples:



Input :  words[] = {"zy", "ab"}
Output : zabcdefghijklmnopqrstuvwxy
Basically we need to make sure that 'z' comes
before 'a'.

Input :  words[] = {"geeks", "gamers", "coders", 
                    "everyoneelse"}
Output : zyxwvutsrqponmlkjihgceafdb

Input : words[] = {"marvel", "superman", "spiderman", 
                                           "batman"
Output : zyxwvuptrqonmsbdlkjihgfeca

Naive approach: 

The brute-force approach would be to check all the possible orders, and check if any of them satisfy the given order of words. Considering there are 26 alphabets in the English language, there are 26! number of permutations that can be valid orders. Considering we check every pair for verifying an order, the complexity of this approach goes to O(26!*N^2), which is well beyond practically preferred time complexity. Using topological sort: This solution requires knowledge of Graphs and its representation as adjacency lists, DFS and Topological sorting



In our required order, it is required to print letters such that each letter must be followed by the letters that are placed in lower priority than them. It seems somewhat similar to what topological sort is defined as – In topological sorting, we need to print a vertex before its adjacent vertices. Let’s define each letter in the alphabet as nodes in a standard directed graph. A is said to be connected to B (A—>B) if A precedes B in the order. 

The algorithm can be formulated as follows:

  1. If n is 1, then any order is valid.
  2. Take the first two words. Identify the first different letter (at the same index of the words) in the words. The letter in the first word will precede the letter in the second word.
  3. If there exists no such letter, then the first string must be smaller in length than the second string.
  4. Assign the second word to the first word and input the third word into the second word. Repeat 2, 3 and 4 (n-1) times.
  5. Run a DFS traversal in topological order.
  6. Check if all the nodes are visited. In topological order, if there are cycles in the graph, the nodes in the cycles remain not visited, since it is not possible to visit these nodes after visiting every node adjacent to it. In such a case, order does not exist. In this case, it means that the order in our list contradicts itself.

Implementation:




/* CPP program to find an order of alphabets
so that given set of words are considered
sorted */
#include <bits/stdc++.h>
using namespace std;
#define MAX_CHAR 26
 
void findOrder(vector<string> v)
{
    int n = v.size();
 
    /* If n is 1, then any order works */
    if (n == 1) {
        cout << "abcdefghijklmnopqrstuvwxyz";
        return;
    }
 
    /* Adjacency list of 26 characters*/
    vector<int> adj[MAX_CHAR];
 
    /* Array tracking the number of edges that are
    inward to each node*/
    vector<int> in(MAX_CHAR, 0);
 
    // Traverse through all words in given array
    string prev = v[0];
 
    /* (n-1) loops because we already acquired the
    first word in the list*/
    for (int i = 1; i < n; ++i) {
        string s = v[i];
 
        /* Find first such letter in the present string that is different
        from the letter in the previous string at the same index*/
        int j;
        for (j = 0; j < min(prev.length(), s.length()); ++j)
            if (s[j] != prev[j])
                break;
 
        if (j < min(prev.length(), s.length())) {
 
            /* The letter in the previous string precedes the one
            in the present string, hence add the letter in the present
            string as the child of the letter in the previous string*/
            adj[prev[j] - 'a'].push_back(s[j] - 'a');
 
            /* The number of inward pointing edges to the node representing
            the letter in the present string increases by one*/
            in[s[j] - 'a']++;
 
            /* Assign present string to previous string for the next
            iteration. */
            prev = s;
            continue;
        }
 
        /* If there exists no such letter then the string length of
        the previous string must be less than or equal to the
        present string, otherwise no such order exists*/
        if (prev.length() > s.length()) {
            cout << "Impossible";
            return;
        }
 
        /* Assign present string to previous string for the next
        iteration */
        prev = s;
    }
 
    /* Topological ordering requires the source nodes
    that have no parent nodes*/
    stack<int> stk;
    for (int i = 0; i < MAX_CHAR; ++i)
        if (in[i] == 0)
            stk.push(i);
 
    /* Vector storing required order (anyone that satisfies) */
    vector<char> out;
 
    /* Array to keep track of visited nodes */
    bool vis[26];
    memset(vis, false, sizeof(vis));
 
    /* Standard DFS */
    while (!stk.empty()) {
 
        /* Acquire present character */
        char x = stk.top();
        stk.pop();
 
        /* Mark as visited */
        vis[x] = true;
 
        /* Insert character to output vector */
        out.push_back(x + 'a');
 
        for (int i = 0; i < adj[x].size(); ++i) {
            if (vis[adj[x][i]])
                continue;
 
            /* Since we have already included the present
            character in the order, the number edges inward
            to this child node can be reduced*/
            in[adj[x][i]]--;
 
            /* If the number of inward edges have been removed,
            we can include this node as a source node*/
            if (in[adj[x][i]] == 0)
                stk.push(adj[x][i]);
        }
    }
 
    /* Check if all nodes(alphabets) have been visited.
    Order impossible if any one is unvisited*/
    for (int i = 0; i < MAX_CHAR; ++i)
        if (!vis[i]) {
            cout << "Impossible";
            return;
        }
 
    for (int i = 0; i < out.size(); ++i)
        cout << out[i];
}
 
// Driver code
int main()
{
    vector<string> v{ "efgh", "abcd" };
    findOrder(v);
    return 0;
}




/* Java program to find an order of alphabets
so that given set of words are considered
sorted */
 
import java.util.ArrayList;
import java.util.Stack;
 
public class Sorted {
 
      @SuppressWarnings("unchecked")
    static void findOrder(String[] v)
    {
        int n = v.length;
        int MAX_CHAR = 26;
 
        /* If n is 1, then any order works */
        if (n == 1) {
            System.out.println(
                "abcdefghijklmnopqrstuvwxyz");
            return;
        }
 
        /* Adjacency list of 26 characters*/
        ArrayList<Integer>[] adj = new ArrayList[MAX_CHAR];
        for (int i = 0; i < MAX_CHAR; i++)
            adj[i] = new ArrayList<Integer>();
 
        /* Array tracking the number of edges that are
        inward to each node*/
        int[] in = new int[MAX_CHAR];
 
        // Traverse through all words in given array
        String prev = v[0];
 
        /* (n-1) loops because we already acquired the
        first word in the list*/
        for (int i = 1; i < n; ++i) {
            String s = v[i];
 
            /* Find first such letter in the present string
            that is different from the letter in the
            previous string at the same index*/
            int j;
            for (j = 0;
                 j < Math.min(prev.length(), s.length());
                 ++j)
                if (s.charAt(j) != prev.charAt(j))
                    break;
 
            if (j < Math.min(prev.length(), s.length())) {
 
                /* The letter in the previous string
                precedes the one in the present string,
                hence add the letter in the present string
                as the child of the letter in the previous
                string*/
                adj[prev.charAt(j) - 'a'].add(s.charAt(j)
                                              - 'a');
 
                /* The number of inward pointing edges to
                the node representing the letter in the
                present string increases by one*/
                in[s.charAt(j) - 'a']++;
 
                /* Assign present string to previous string
                for the next iteration. */
                prev = s;
                continue;
            }
 
            /* If there exists no such letter then the
            string length of the previous string must be
            less than or equal to the present string,
            otherwise no such order exists*/
            if (prev.length() > s.length()) {
                System.out.println("Impossible");
                return;
            }
 
            /* Assign present string to previous string for
            the next iteration */
            prev = s;
        }
 
        /* Topological ordering requires the source nodes
        that have no parent nodes*/
        Stack<Integer> stk = new Stack<Integer>();
        for (int i = 0; i < MAX_CHAR; ++i)
            if (in[i] == 0)
                stk.push(i);
 
        /* Vector storing required order (anyone that
         * satisfies) */
        ArrayList<Character> out
            = new ArrayList<Character>();
 
        /* Array to keep track of visited nodes */
        boolean[] vis = new boolean[26];
 
        /* Standard DFS */
        while (!stk.empty()) {
 
            /* Acquire present character */
            int x = stk.peek();
            stk.pop();
 
            /* Mark as visited */
            vis[x] = true;
 
            /* Insert character to output vector */
            out.add((char)((char)x + 'a'));
 
            for (int i = 0; i < adj[x].size(); ++i) {
                if (vis[adj[x].get(i)])
                    continue;
 
                /* Since we have already included the
                present character in the order, the number
                edges inward to this child node can be
                reduced*/
                in[adj[x].get(i)]--;
 
                /* If the number of inward edges have been
                removed, we can include this node as a
                source node*/
                if (in[adj[x].get(i)] == 0)
                    stk.push(adj[x].get(i));
            }
        }
 
        /* Check if all nodes(alphabets) have been visited.
        Order impossible if any one is unvisited*/
        for (int i = 0; i < MAX_CHAR; ++i)
            if (!vis[i]) {
                System.out.println("Impossible");
                return;
            }
 
        for (int i = 0; i < out.size(); ++i)
            System.out.print(out.get(i));
    }
 
    // Driver code
    public static void main(String[] args)
    {
        String[] v = { "efgh", "abcd" };
        findOrder(v);
    }
}
 
// This code is contributed by Lovely Jain




# Python program to find an order of alphabets
# so that given set of words are considered
# sorted
MAX_CHAR = 26
 
def findOrder(v):
    n = len(v)
 
    # If n is 1, then any order works
    if (n == 1):
        print("abcdefghijklmnopqrstuvwxyz")
        return
 
    # Adjacency list of 26 characters
    adj = [[] for i in range(MAX_CHAR)]
 
    # Array tracking the number of edges that are
    # inward to each node
    In = [0 for i in range(MAX_CHAR)]
 
    # Traverse through all words in given array
    prev = v[0]
 
    # (n-1) loops because we already acquired the
    # first word in the list
    for i in range(1,n):
        s = v[i]
 
        # Find first such letter in the present string that is different
        # from the letter in the previous string at the same index
        for j in range(min(len(prev), len(s))):
            if (s[j] != prev[j]):
                break
 
        if (j < min(len(prev), len(s))):
 
            # The letter in the previous string precedes the one
            # in the present string, hence add the letter in the present
            # string as the child of the letter in the previous string
            adj[ord(prev[j]) - ord('a')].append(ord(s[j]) - ord('a'))
 
            # The number of inward pointing edges to the node representing
            # the letter in the present string increases by one
            In[ord(s[j]) - ord('a')] += 1
 
            # Assign present string to previous string for the next
            # iteration.
            prev = s
            continue
 
        # If there exists no such letter then the string length of
        # the previous string must be less than or equal to the
        # present string, otherwise no such order exists*
        if (len(prev) > len(s)):
            print("Impossible")
            return
 
        # Assign present string to previous string for the next
        # iteration
        prev = s
 
    # Topological ordering requires the source nodes
    # that have no parent nodes
    stk = []
    for i in range(MAX_CHAR):
        if (In[i] == 0):
            stk.append(i)
 
    # Vector storing required order (anyone that satisfies) */
    out = []
 
    # Array to keep track of visited nodes */
    vis = [False for i in range(26)]
 
    # Standard DFS */
    while (len(stk) > 0):
 
        # Acquire present character */
        x = stk.pop()
 
        # Mark as visited */
        vis[x] = True
 
        # Insert character to output vector */
        out.append(chr(x + ord('a')))
 
        for i in range(len(adj[x])):
            if (vis[adj[x][i]]):
                continue
 
            # Since we have already included the present
            # character in the order, the number edges inward
            # to this child node can be reduced
            In[adj[x][i]] -= 1
 
            # If the number of inward edges have been removed,
            # we can include this node as a source node
            if (In[adj[x][i]] == 0):
                stk.append(adj[x][i])
 
    # Check if all nodes(alphabets) have been visited.
    # Order impossible if any one is unvisited
    for i in range(MAX_CHAR):
        if (vis[i] == 0):
            print("Impossible")
            return
 
    for i in range(len(out)):
        print(out[i],end="")
 
# Driver code
 
v = [ "efgh", "abcd" ]
findOrder(v)
 
# This code is contributed by shinjanpatra




/* C# program to find an order of alphabets
so that given set of words are considered
sorted */
using System;
using System.Collections.Generic;
 
class Sorted {
  static void FindOrder(string[] v)
  {
    int n = v.Length;
    int MAX_CHAR = 26;
     
    /* If n is 1, then any order works */
    if (n == 1) {
      Console.WriteLine("abcdefghijklmnopqrstuvwxyz");
      return;
    }
     
    /* Adjacency list of 26 characters*/
    List<int>[] adj = new List<int>[ MAX_CHAR ];
    for (int i = 0; i < MAX_CHAR; i++)
      adj[i] = new List<int>();
     
    /* Array tracking the number of edges that are
         inward to each node*/
    int[] ino = new int[MAX_CHAR];
     
    // Traverse through all words in given array
    string prev = v[0];
     
    /* (n-1) loops because we already acquired the
          first word in the list*/
    for (int i = 1; i < n; ++i) {
      string s = v[i];
       
      /* Find first such letter in the present string
               that is different from the letter in the
               previous string at the same index*/
      int j;
      for (j = 0; j < Math.Min(prev.Length, s.Length);
           ++j)
        if (s[j] != prev[j])
          break;
 
      if (j < Math.Min(prev.Length, s.Length)) {
         
        /* The letter in the previous string
       precedes the one in the present string, hence add the
       letter in the present string as the child of the
       letter in the previous string*/
        adj[prev[j] - 'a'].Add(s[j] - 'a');
         
        /* The number of inward pointing edges to
      the node representing
      the letter in the present string increases by one*/
        ino[s[j] - 'a']++;
        prev = s;
        continue;
      }
 
      if (prev.Length > s.Length) {
        Console.WriteLine("Impossible");
        return;
      }
       
      /* Assign present string to previous string for
               the next iteration. */
      prev = s;
    }
 
    /* Topological ordering requires the source nodes
        that have no parent nodes*/
    Stack<int> stk = new Stack<int>();
    for (int i = 0; i < MAX_CHAR; ++i)
      if (ino[i] == 0)
        stk.Push(i);
     
    /* Vector storing required order (anyone that
         * satisfies) */
    List<char> outo = new List<char>();
     
    /* Array to keep track of visited nodes */
    bool[] vis = new bool[26];
     
    /* Standard DFS */
    while (stk.Count > 0) {
      int x = stk.Peek();
      stk.Pop();
 
      vis[x] = true;
 
      outo.Add((char)((char)x + 'a'));
 
      for (int i = 0; i < adj[x].Count; ++i) {
        if (vis[adj[x][i]])
          continue;
 
        ino[adj[x][i]]--;
 
        if (ino[adj[x][i]] == 0)
          stk.Push(adj[x][i]);
      }
    }
     
    /* Check if all nodes(alphabets) have been visited.
          Order impossible if any one is unvisited*/
    for (int i = 0; i < MAX_CHAR; ++i)
      if (!vis[i]) {
        Console.WriteLine("Impossible");
        return;
      }
 
    foreach(char c in outo) Console.Write(c);
  }
   
  // Driver code
  static void Main(string[] args)
  {
    string[] v = { "efgh", "abcd" };
    FindOrder(v);
  }
}
 
// This code is contributed by lokeshpotta20.




<script>
 
/* JavaScript program to find an order of alphabets
so that given set of words are considered
sorted */
 
const MAX_CHAR = 26
 
function findOrder(v)
{
    let n = v.length;
 
    /* If n is 1, then any order works */
    if (n == 1) {
        document.write("abcdefghijklmnopqrstuvwxyz");
        return;
    }
 
    /* Adjacency list of 26 characters*/
    let adj = new Array(MAX_CHAR).fill(0).map(()=>[]);
 
    /* Array tracking the number of edges that are
    inward to each node*/
    let In = new Array(MAX_CHAR).fill(0);
 
    // Traverse through all words in given array
    let prev = v[0];
 
    /* (n-1) loops because we already acquired the
    first word in the list*/
    for (let i = 1; i < n; ++i) {
        let s = v[i];
 
        /* Find first such letter in the present string that is different
        from the letter in the previous string at the same index*/
        let j;
        for (j = 0; j < Math.min(prev.length, s.length); ++j)
            if (s[j] != prev[j])
                break;
 
        if (j < Math.min(prev.length, s.length)) {
 
            /* The letter in the previous string precedes the one
            in the present string, hence add the letter in the present
            string as the child of the letter in the previous string*/
            adj[prev.charCodeAt(j) - 'a'.charCodeAt(0)].push(s.charCodeAt(j) - 'a'.charCodeAt(0));
 
            /* The number of inward pointing edges to the node representing
            the letter in the present string increases by one*/
            In[s.charCodeAt(j) - 'a'.charCodeAt(0)]++;
 
            /* Assign present string to previous string for the next
            iteration. */
            prev = s;
            continue;
        }
 
        /* If there exists no such letter then the string length of
        the previous string must be less than or equal to the
        present string, otherwise no such order exists*/
        if (prev.length > s.length) {
            document.write("Impossible");
            return;
        }
 
        /* Assign present string to previous string for the next
        iteration */
        prev = s;
    }
 
    /* Topological ordering requires the source nodes
    that have no parent nodes*/
    let stk = [];
    for (let i = 0; i < MAX_CHAR; ++i)
        if (In[i] == 0)
            stk.push(i);
 
    /* Vector storing required order (anyone that satisfies) */
    let out = [];
 
    /* Array to keep track of visited nodes */
    let vis = new Array(26).fill(false);
 
    /* Standard DFS */
    while (stk.length > 0) {
 
        /* Acquire present character */
        let x = stk.pop();
 
        /* Mark as visited */
        vis[x] = true;
 
        /* Insert character to output vector */
        out.push(String.fromCharCode(x + 'a'.charCodeAt(0)));
 
        for (let i = 0; i < adj[x].length; ++i) {
            if (vis[adj[x][i]])
                continue;
 
            /* Since we have already included the present
            character in the order, the number edges inward
            to this child node can be reduced*/
            In[adj[x][i]]--;
 
            /* If the number of inward edges have been removed,
            we can include this node as a source node*/
            if (In[adj[x][i]] == 0)
                stk.push(adj[x][i]);
        }
    }
 
    /* Check if all nodes(alphabets) have been visited.
    Order impossible if any one is unvisited*/
    for (let i = 0; i < MAX_CHAR; ++i)
        if (!vis[i]) {
            document.write("Impossible");
            return;
        }
 
    for (let i = 0; i < out.length; ++i)
        document.write(out[i]);
}
 
// Driver code
 
let v = [ "efgh", "abcd" ];
findOrder(v);
 
// This code is contributed by shinjanpatra
 
</script>

Output
zyxwvutsrqponmlkjihgfeadcb

The complexity of this approach is O(N*|S|) + O(V+E), where |V|=26 (number of nodes is the same as number of alphabets) and |E|<N (since at most 1 edge is created for each word as input). Hence overall complexity is O(N*|S|+N). |S| represents the length of each word.

Auxiliary Space: O(MAX_CHAR)


Article Tags :