Given an array of strings, find if the strings can be chained to form a circle


Given an array of strings, find if the strings can be chained to form a circle | GeeksforGeeks
Given an array of strings, find if the given strings can be chained to form a circle. A string X can be put before another string Y in circle if the last character of X is same as first character of Y.


The idea is to create a graph of all characters and then find if their is an eulerian circuit in the graph or not. If there is an eulerian circuit, then chain can be formed, otherwise not.
Note that a graph has eulerian circuit only if all vertices are of even degree.
Following are detailed steps of the algorithm.
1) Create a graph g with number of vertices equal to the size of alphabet in the given alien language. We have created a graph with 26 vertices in the below program.
2) Do following for every string in the given array of strings.
…..a) Add an edge from first character to last character of the given graph.
3) If the created graph has eulerian circuit, then return true, else return false.

The edges represents one word, vertexes can be shared.

#define CHARS 26
bool canBeChained(string arr[], int n)
{
    // Create a graph with 'aplha' edges
    Graph g(CHARS);
    // Create an edge from first character to last character
    // of every string
    for (int i = 0; i < n; i++)
    {
        string s = arr[i];
        g.addEdge(s[0]-'a', s[s.length()-1]-'a');
    }
    // The given array of strings can be chained if there
    // is an eulerian cycle in the created graph
    return g.isEulerianCycle();
}
class Graph
{
    int V;    // No. of vertices
    list<int> *adj;    // A dynamic array of adjacency lists

void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w);
    adj[w].push_back(v);  // Note: the graph is undirected
}

/* This function returns true if the graph has an eulerian

   cycle, otherwise returns false  */

bool Graph::isEulerianCycle()

{

    // Check if all non-zero degree vertices are connected

    if (isConnected() == false)

        return 0;

    // Check if there is a vertex with odd degree
    for (int i = 0; i < V; i++)
        if (adj[i].size() & 1)
            return false;
    return true; // If all vertices are of even degree
}
bool Graph::isConnected()
{
    // Mark all the vertices as not visited
    bool visited[V];
    int i;
    for (i = 0; i < V; i++)
        visited[i] = false;
    // Find a vertex with non-zero degree
    for (i = 0; i < V; i++)
        if (adj[i].size() != 0)
            break;
    // If there are no edges in the graph, return true
    if (i == V)
        return true;
    // Start DFS traversal from a vertex with non-zero degree
    DFSUtil(i, visited);
    // Check if all non-zero degree vertices are visited
    for (i = 0; i < V; i++)
        if (visited[i] == false && adj[i].size() > 0)
            return false;
    return true;
}
https://github.com/mission-peace/interview/blob/master/src/com/interview/recursion/ChainWordsToFormCircle.java
    public List<String> formCircle(String input[]){
        List<String> result = new ArrayList<String>();
        //since chain is a circle any point can be the start point of the chain.
        //We make input[0] as start point
        result.add(input[0]);
        boolean used[] = new boolean[input.length];
        boolean r = formCircle(input,result,used,input[0].charAt(0));
        if(!r){
            return Collections.emptyList();
        }
        return result;
    }
 
    //we keep track of first char of the chain and the end compare that with last char of last element of the chain
    private boolean formCircle(String input[], List<String> result,boolean used[],char firstChar){
        if(input.length == result.size()){
            String str = result.get(result.size()-1);
            if(firstChar == str.charAt(str.length()-1)){
                return true;
            }
            return false;
        }
        String str = result.get(result.size()-1);
        char lastChar = str.charAt(str.length()-1);
        for(int i=1; i < input.length; i++){
            if(used[i]){
                continue;
       
            }
            if(lastChar == input[i].charAt(0)){
                used[i] = true;
                result.add(input[i]);
                boolean r = formCircle(input,result,used,firstChar);
                if(r){
                    return true;
                }
                used[i] = false;
                result.remove(result.size()-1);
            }
         
        }
        return false;
    }
Eulerian path and circuit for undirected graph
http://www.geeksforgeeks.org/eulerian-path-and-circuit/
Eulerian Cycle
An undirected graph has Eulerian cycle if following two conditions are true.
….a) All vertices with non-zero degree are connected. We don’t care about vertices with zero degree because they don’t belong to Eulerian Cycle or Path (we only consider all edges). // one dfs
….b) All vertices have even degree. // We should do this step first. much faster.
Eulerian Path
An undirected graph has Eulerian Path if following two conditions are true.
….a) Same as condition (a) for Eulerian Cycle
….b) If zero or two vertices have odd degree and all other vertices have even degree. Note that only one vertex with odd degree is not possible in an undirected graph (sum of all degrees is always even in an undirected graph)

Euler Circuit in a Directed Graph
http://www.geeksforgeeks.org/euler-circuit-directed-graph/
A directed graph has an eulerian cycle if following conditions are true (Source: Wiki)
1) All vertices with nonzero degree belong to a single strongly connected component. //one dfs and one dfs for its transpose graph
2) In degree and out degree of every vertex is same. // do this fisrt
https://reeestart.wordpress.com/2016/06/14/gfg-check-if-strings-can-be-chained-to-form-a-cycle/

  public boolean hasCircle(String[] words) {
    if (words.length == 0) {
      return false;
    }
    Map<Character, Set<Character>> graph = new HashMap<Character, Set<Character>>();
    int[] inDegree = new int[26];
    for (String str : words) {
      char first = str.charAt(0);
      char last = str.charAt(str.length() - 1);
      if (!graph.containsKey(first)) {
        graph.put(first, new HashSet<Character>());
      }
      graph.get(first).add(last);
      inDegree[last - 'a']++;
    }
    return isEulerianCycle(graph, inDegree);
  }
  /* An directed graph has an Eulerian cycle if and only if every */
  /* vertex has the indegree and outdegree */
  private boolean isEulerianCycle(Map<Character, Set<Character>> graph, int[] inDegree) {
    if (!isStronglyConnect(graph)) {
      return false;
    }
    for (char c : graph.keySet()) {
      if (graph.get(c).size() != inDegree[c - 'a']) {
        return false;
      }
    }
    return true;
  }
  /* A directed graph is called strongly connected if there is a path in each direction between each pair of vertices of the graph */
  private boolean isStronglyConnect(Map<Character, Set<Character>> graph) {
    boolean[] isVisited = new boolean[26];
    for (char c : graph.keySet()) {
      if (graph.get(c).size() != 0) {
        dfs(c, graph, isVisited);
        break;
      }
    }
    for (char c : graph.keySet()) {
      if (!isVisited[c - 'a']) {
        return false;
      }
    }
    Map<Character, Set<Character>> reversedGraph = new HashMap<Character, Set<Character>>();
    for (char ori : graph.keySet()) {
      for (char dest : graph.get(ori)) {
        if (!reversedGraph.containsKey(dest)) {
          reversedGraph.put(dest, new HashSet<Character>());
        }
        reversedGraph.get(dest).add(ori);
      }
    }
    Arrays.fill(isVisited, false);
    for (char c : reversedGraph.keySet()) {
      if (reversedGraph.get(c).size() != 0) {
        dfs(c, reversedGraph, isVisited);
        break;
      }
    }
    for (char c : reversedGraph.keySet()) {
      if (!isVisited[c - 'a']) {
        return false;
      }
    }
    return true;
  }
  private void dfs(char c, Map<Character, Set<Character>> graph, boolean[] isVisited) {
    isVisited[c - 'a'] = true;
    for (char adj : graph.get(c)) {
      if (!isVisited[adj - 'a']) {
        dfs(adj, graph, isVisited);
      }
    }
  }

http://www.geeksforgeeks.org/find-array-strings-can-chained-form-circle-set-2/
We solve this problem by treating this as a graph problem, where vertices will be first and last character of strings and we will draw an edge between two vertices if they are first and last character of same string, so number of edges in graph will be same as number of strings in the array.

Now it can be clearly seen after graph representation that if a loop among graph vertices is possible then we can reorder the strings otherwise not. As in above diagram’s example a loop can be found in first and third array of string but not in second array of string. Now to check whether this graph can have a loop which goes through all the vertices, we’ll check two conditions,
1) Indegree and Outdegree of each vertex should be same.
2) Graph should be strongly connected.


First condition can be checked easily by keeping two arrays, in and out for each character. For checking whether graph is having a loop which goes through all vertices is same as checking complete directed graph is strongly connected or not because if it has a loop which goes through all vertices then we can reach to any vertex from any other vertex that is, graph will be strongly connected and same argument can be given for reverse statement also.
Now for checking second condition we will just run a DFS from any character and visit all reachable vertices from this, now if graph has a loop then after this one DFS all vertices should be visited, if all vertices are visited then we will return true otherwise false so visiting all vertices in a single DFS flags a possible ordering among strings.
//  Utility method for a depth first search among vertices
void dfs(vector<int> g[], int u, vector<bool> &visit)
{
    visit[u] = true;
    for (int i = 0; i < g[u].size(); ++i)
        if(!visit[g[u][i]])
            dfs(g, g[u][i], visit);
}
//  Returns true if all vertices are strongly connected
// i.e. can be made as loop
bool isConnected(vector<int> g[], vector<bool> &mark, int s)
{
    // Initialize all vertices as not visited
    vector<bool> visit(M, false);
    //  perform a dfs from s
    dfs(g, s, visit);
    //  now loop through all characters
    for (int i = 0; i < M; i++)
    {
        /*  I character is marked (i.e. it was first or last
            character of some string) then it should be
            visited in last dfs (as for looping, graph
            should be strongly connected) */
        if (mark[i] && !visit[i])
            return false;
    }
    //  If we reach that means graph is connected
    return true;
}
//  return true if an order among strings is possible
bool possibleOrderAmongString(string arr[], int N)
{
    // Create an empty graph
    vector<int> g[M];
    // Initialize all vertices as not marked
    vector<bool> mark(M, false);
    // Initialize indegree and outdegree of every
    // vertex as 0.
    vector<int> in(M, 0), out(M, 0);
    // Process all strings one by one
    for (int i = 0; i < N; i++)
    {
        // Find first and last characters
        int f = arr[i].front() - 'a';
        int l = arr[i].back() - 'a';
        // Mark the characters
        mark[f] = mark[l] = true;
        //  increase indegree and outdegree count
        in[f]++;
        out[l]++;
        // Add an edge in graph
        g[f].push_back(l);
    }
    // If for any character indegree is not equal to
    // outdegree then ordering is not possible
    for (int i = 0; i < M; i++)
        if (in[i] != out[i])
            return false;
    return isConnected(g, mark, arr[0].front() - 'a');
}

X. Extend
https://reeestart.wordpress.com/2016/06/14/google-longest-string-pair-that-forms-a-cycle/
给一个 sorted dictionary, [about, apple, google, leg, lemma, time]
要求返回最长的一对pair,使得Pair中的两个单词满足:
第一个单词的最后两个字母等于第二个单词的头两个
第二个单词的最后一个字母等于第一个单词的头一个
就相当于形成一个cycle。
GFG – Check if Strings can be Chained to Form a Cycle 可以算这题的一个follow up。这题用hash map就可以搞定了,但是GFG这题还是比较难的。
[Solution]
最直接的办法就是用一个hash table来hash头两个字母以及单词。然后遍历一遍dict找最长的pair。这种做法时间复杂度为O(n),空间也为O(n)。
不过注意题目写了是sorted dictionary. 如果被问到有没有其他方法?自然会想到binary search.
如果再仔细想想这道题的binary search并不能优化时间复杂度,反而降低了。只有在空间上有所提升。
[Solution #1] – binary search
对于每个字典里的string,根据它的最后两个字母,在整个字典里做binary search。
但是当mid的头两个字母和curr的后两个字母一样时,不能直接丢掉左边或右边,得从mid开始往两边扫。所以这样binary search的时间复杂度为O(n), 即使用递归binary search, 递归式为T(n) = 2T(n / 2),画个tree就知道也是O(n).
这样每个string做一遍binary search就是O(n^2).
不过递归binary search的代码不大写,值得好好看看。
[Solution #2] – hash table
// O(n^2) time, O(logn) space
class Solution {
  int result = 0;
 
  public int longestPair(String[] dict) {
    if (dict == null || dict.length == 0) {
      return 0;
    }
 
    for (String word : dict) {
      String end = word.substring(word.length() - 2, word.length());
      char first = word.charAt(0);
      binarySearch(word, end, first, 0, dict.length - 1, dict);
    }
    return result;
  }
 
  private void binarySearch(String word, String start, char last, int l, int r, String[] dict) {
    if (l > r) {
      return;
    }
 
    while (l <= r) {
      int mid = (l + r) / 2;
      String curr = dict[mid];
 
      if (curr.startsWith(start)) {
        if (curr.charAt(curr.length() - 1) == last) {
          result = Math.max(result, word.length() + curr.length());
        }
        binarySearch(word, start, last, l, mid - 1, dict);
        binarySearch(word, start, last, mid + 1, r, dict);
        return;
      }
      else if (curr.substring(0, 2).compareTo(start) < 0) {
        l = mid + 1;
      }
      else {
        r = mid - 1;
      }
    }
  }
}
 
 
/*
  If the dict is not sorted, then HashMap is required
  O(n) time, O(n) space
*/
class Solution2 {
  public int longestPair(String[] dict) {
    if (dict == null || dict.length == 0) {
      return 0;
    }
 
    Map<String, List<Integer>> map = new HashMap<>();
    for (int i = 0; i < dict.length; i++) {
      String word = dict[i];
      int len = word.length();
      if (len <= 2) continue;
      String key = word.substring(len - 2, len)  + word.charAt(0);
      map.putIfAbsent(key, new ArrayList<>());
      map.get(key).add(i);
    }
 
    int result = 0;
    for (int i = 0; i < dict.length; i++) {
      String word = dict[i];
      int len = word.length();
      String _key = word.substring(0, 2) + word.charAt(word.length() - 1);
      if (map.containsKey(_key)) {
        for (int j : map.get(_key)) {
          result = Math.max(result, len + dict[j].length());
        }
      }
    }
    return result;
  }
}
Read full article from Given an array of strings, find if the strings can be chained to form a circle | 
GeeksforGeeks

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts