Skip to content

Instantly share code, notes, and snippets.

@lienista
Created October 23, 2018 02:11
Show Gist options
  • Save lienista/c8a4619818d99fb5a17c6fecfaddce88 to your computer and use it in GitHub Desktop.
Save lienista/c8a4619818d99fb5a17c6fecfaddce88 to your computer and use it in GitHub Desktop.
(Algorithms in Javascript) Practice. A precedence rule is given as "P>E", which means that letter "P" is followed by letter "E". Write a function, given an array of precedence rules, that finds the word represented by the given rules. Note: Each represented word contains a set of unique characters, i.e. the word does not contain duplicate letters.
/*
we create 2 separate arrays of letters and count
the number of characters resulting from the
original precedence array.
we look up the index of each letter from first letter
array and follow the index of the next letter.
*/
function findWord(a){
console.log(a);
let split = [];
let len = a.length;
let original = a;
let firstLetter = [],
secondLetter =[],
currentIndex = 0,
count = {},
letter;
//count the number of repetitions for each letter
while(currentIndex<len){
firstLetter.push(a[currentIndex].charAt(0));
secondLetter.push(a[currentIndex].charAt(2));
recordLetter(count, a[currentIndex].charAt(0), a[currentIndex].charAt(2));
//console.log(count);
currentIndex++;
}
//console.log(firstLetter, secondLetter, count);
//The first letter should be in firstLetter array
//and has count of 1.
let first;
for(let c in count){
if(count[c] === 1){
if(firstLetter.indexOf(c) >= 0) first = c;
}
}
let result = first;
currentIndex = firstLetter.indexOf(first);
let times = 0;
while(times < len){
result += secondLetter[currentIndex];
currentIndex = firstLetter.indexOf(secondLetter[currentIndex]);
times++;
}
console.log(result);
return result;
}
function recordLetter(count, letter1, letter2){
count[letter1] = count[letter1] ? count[letter1]+1 : 1;
count[letter2] = count[letter2] ? count[letter2]+1 : 1;
return count;
}
findWord(["P>E", "E>R","R>U"]) // PERU
findWord(["I>N", "A>I","P>A","S>P"]) // SPAIN
findWord(["U>N", "G>A", "R>Y", "H>U", "N>G", "A>R"]) // HUNGARY
findWord(["I>F", "W>I", "S>W", "F>T"]) // SWIFT
findWord(["R>T", "A>L", "P>O", "O>R", "G>A", "T>U", "U>G"]) // PORTUGAL
findWord(["U>N", "G>A", "R>Y", "H>U", "N>G", "A>R"]) // HUNGARY
findWord(["I>F", "W>I", "S>W", "F>T"]) // SWIFT
findWord(["R>T", "A>L", "P>O", "O>R", "G>A", "T>U", "U>G"]) // PORTUGAL
findWord(["W>I", "R>L", "T>Z", "Z>E", "S>W", "E>R", "L>A", "A>N", "N>D", "I>T"]) // SWITZERLAND
@pauriosa-papa
Copy link

Could you share the source of the problem. I want to find similar problems.

Yeah me too!

@ahmadalibaloch
Copy link

ahmadalibaloch commented Jun 14, 2022

It was also asked by Toptal in their technical interview, to be solved in 15 minutes.

@ahmadalibaloch
Copy link

ahmadalibaloch commented Jun 14, 2022

Javascript Readable Solution with explanation:

const findWord = (rulesArray) => {
    // this problem is solved by building a precedence (rules) hash map of which letter points to which
    // then we find first letter; a letter that does'nt appear as a second letter in any rule
    // then we iterate through the rules and build the word by following hash map
    // the algo for word looks like this first letter = key -> value -> key -> value

    const hashMap = {};
    rulesArray.forEach(([leftLetter, _, rightLetter]) => {
        hashMap[leftLetter] = rightLetter
    })

    let startingLetter = Object.keys(hashMap).find(letter => !Object.values(hashMap).includes(letter));
    let word = startingLetter;

    rulesArray.forEach(_ => {
        word += hashMap[startingLetter];
        startingLetter = hashMap[startingLetter];
    });

    return word;
}

@Iamshankhadeep
Copy link

Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};

const findWord = (arr) => {
    let sol = []
    for(let i=0;i<100;i++){
        const element = arr[i]
        if(!element){
            break;
        }
        const splitArr = element.split('>')
        if(sol.length <=0){
            sol = [...splitArr]
        }else{
            const index = sol.indexOf(splitArr[0])
            if(index >= 0){
                sol.insert(index+1, splitArr[1])
            }else{
                const seconIndcex = sol.indexOf(splitArr[1])
                if(seconIndcex >=0){
                    sol.insert(seconIndcex,splitArr[0])
                }else{
                    arr.push(element)
                }
            }
        }
    }
    return sol.join('')
}

@fuzzmonkey
Copy link

A ruby version

def findWord(rules)
  nodes = {}
  end_letters = []
  start_letters = []

  rules.each do |rule|
    first, second = rule.split(">")

    start_letters << first
    end_letters << second

    nodes[first] = second
  end 

  start_letter = start_letters.find{|ltr| !end_letters.include?(ltr) }

  word = ""
  current_letter = start_letter

  loop do
    break if current_letter.nil?

    word += current_letter

    current_letter = nodes[current_letter]
  end

  word
end

@hbisxy
Copy link

hbisxy commented Aug 13, 2022

This is my answer is python. Not as smart as the solutions above, but thought i might share :)

def findChar(c, d):
    for i in d:
        if c in i:
            return d.index(i)
    return -1

findChar("U", [["W",'I'],['U']])

def findWord(rules):
    d = []
    count = 0
    for r in rules:
        ch = r.split(">")
        a = ch[0]
        b = ch[1]
        print(a, b)
        a_pos = findChar(a, d)
        b_pos = findChar(b, d)
        if a_pos == -1 and b_pos == -1:
            d.append([a,b])
        elif a_pos != -1 and b_pos == -1:
            d[a_pos].append(b)
        elif b_pos != -1 and a_pos == -1:
            d[b_pos].insert(0, a)
        elif a_pos > b_pos:
            d[a_pos], d[b_pos] = d[b_pos], d[a_pos]
    return "".join([x for i in d for x in i])
            
        
        
findWord(["W>I", "R>L", "T>Z", "Z>E", "S>W", "E>R", "L>A", "A>N", "N>D", "I>T"])

@hbisxy
Copy link

hbisxy commented Aug 14, 2022

findWord(["A>B","C>D","A>C","D>B"])

The above solutions don't work for this edge case. Expected answer should be ACDB.

Still thinking of the final solution...

@hbisxy
Copy link

hbisxy commented Aug 14, 2022

This is a Topological sorting

using Kahn's algorithm


def findWord(st):
    left = [x[0] for x in st]
    right = [y[-1] for y in st]
    L = []
    S = list(set(left) - set(right))

    while len(S) > 0:  #while S is not empty do
        n = S.pop()  #remove a node n from S
        L.append(n)  #add n to L
        
        for i in range(len(left)): # for each node m with an edge e from n to m do
            m = right[i]
            if left[i] == n:
                #remove edge e from the graph
                left[i] = 0
                right[i] = 0
            if m not in right: #if m has no other incoming edges then
                S.append(m)  #insert m into S

    if any(left): #if graph has edges then
        return "graph has at least one cycle"
    else:
        return L   #(a topologically sorted order)
    
findWord(["W>I", "R>L", "T>Z", "Z>E", "S>W", "E>R", "L>A", "A>N", "N>D", "I>T"])

findWord(["A>B","C>D","A>C","D>B"])

@josephtesla
Copy link

import collections


def find_word(array):
  graph = collections.defaultdict(list)
  for pair in array:
    [prev, next] = pair.split(">")
    graph[prev].append(next)

  res = ""
  visited = set()
  def topo_dfs(node):
    nonlocal res
    if node in visited:
      return

    visited.add(node)
    for nei in graph[node]:
      if nei not in visited:
        topo_dfs(nei)

    res = node + res


  for ch in list(graph.keys()):
    if ch not in visited:
      topo_dfs(ch)

  return res

@gustborges
Copy link

I've just found your repo in a Google search. I was trying to solve this one. Here is my answer in Ruby.

def find_word(arr)
  # find initial string
  last_letters = arr.map { |str| str[2] }
  first_letters = arr.map { |str| str[0] }
  initial = first_letters - last_letters
  initial_str = arr.filter { |str| str.start_with?(*initial) }.first
  
  # add first string to new_arr and delete it from the original arr
  new_arr = []
  new_arr.push(initial_str[0], initial_str[2])
  arr.delete(initial_str)

  # iterate, get next char and add to new_arr
  while arr.length >= 1
    arr.each do |str|
      if str.start_with?(new_arr.last)
        new_arr << str[2]
        arr.delete(str)
      end
    end
  end

  return new_arr.join
end

@sundarrkshan
Copy link

I was asked to write a solution for this problem in just 25 minutes. But, I could not get selected by Toptal. It took me one day to solve the problem partially. I will post coding using java soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment