Google Interview Question for Software Developers


Country: United States
Interview Type: Phone Interview




Comment hidden because of low score. Click to expand.
2
of 2 vote

You can rotate all strings in the input list such that they all begin with the character "A". Afterwards, it's a simple matter of printing all pairs of duplicate strings (mapped to their original string).

This has a runtime of O(n).

def rotatate_to_A(str):
    if len(str) == 0:
        return ""
    delta = ord('A') - ord(str[0])
    output = ""
    for char in str:
        new_char = ord(char) + delta
        if new_char < ord('A'):
            new_char += 26
        output += chr(new_char)

    return output

def rot_finder(lst):
    rotated_lst = [rotatate_to_A(str) for str in lst]

    dict = {}
    output = []

    for i in range(len(rotated_lst)):
        rotated_word = rotated_lst[i]
        if rotated_word in dict:
            value = dict[rotated_word]
            for index in value:
                original_word_1 = lst[index]
                original_word_2 = lst[i]
                output.append([original_word_1, original_word_2])
            value.append(i)
            dict[rotated_word] = value
        else:
            dict[rotated_word] = [i]

    return output

- Anonymous May 04, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
1
of 1 vote

I think that the output should also contain [bc, za] as it corresponds to ROT_2(ZA) = BC

public class RotFinder {
    String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    Set<Pair<String>> findRotationPairs(List<String> input) {
        Set<Pair<String>> pairs = new HashSet<>();
        for (int i = 0 ; i < input.size() ; i++) {
            for (int j = i + 1 ; j < input.size() ; j++) {
                String source = input.get(i);
                String target = input.get(j);
                if (source.length() < 1) {
                    break;
                }
                if (source.length() == target.length()) {
                    char sourceLetter= source.charAt(0);
                    char targetLetter = target.charAt(0);
                    int rot = findRot(sourceLetter, targetLetter);
                    boolean recordPair = false;
                    for (int k = 1 ; k < source.length() ; k++) {
                        sourceLetter = source.charAt(k);
                        targetLetter = target.charAt(k);
                        int sourceIndex = alpha.indexOf(sourceLetter);
                        int targetIndex = (sourceIndex + rot) % 26;
                        if (targetLetter == alpha.charAt(targetIndex)) {
                            if (k == source.length() - 1) {
                                recordPair = true;
                            }
                        } else {
                            break;
                        }
                    }
                    if (recordPair) {
                        pairs.add(new Pair<>(source, target));
                    }
                }
            }
        }
        return pairs;
    }

    int findRot(char source, char target) {
        for (int i = 0 ; i < 26 ; i++) {
            char checked = alpha.charAt((alpha.indexOf(source) + i) % 26);
            if (checked == target) {
                return i;
            }
        }
        return -1;
    }

    class Pair<T> {
        T first;
        T second;

        Pair(T first, T second) {
            this.first = first;
            this.second = second;
        }

        public String toString() {
            return "[" + first + ", " + second + "] ";
        }
    }

    public static void main (String[] args) {
        List<String> strings = new LinkedList<>();
        strings.add("AB");
        strings.add("BC");
        strings.add("FOO");
        strings.add("ZA");
        strings.add("BAZ");
        strings.add("IRR");
        System.out.println(Arrays.toString(new RotFinder().findRotationPairs(strings).toArray()));
    }

}

- vroom April 22, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
1
of 1 vote

Maybe something like this?

def source_and_rotation(words: List[str]) -> List[List[str]]:

    def rot_char_by(c, i):
        shift = ord('A')
        return chr((((ord(c) - shift) + i) % N) + shift)

    N = len(string.ascii_lowercase)

    transformed_to_origins = defaultdict(set)

    for w in words:
        for i in range(1, N):
            transformed_to_origins["".join([rot_char_by(c, i) for c in w])].add(w)

    res = []
    in_result = set()

    for w in words:
        if w in transformed_to_origins:
            for t in transformed_to_origins[w]:
                if t != w and (w, t) not in in_result:
                    res.append([w, t])
                    in_result.add((w, t)), in_result.add((t, w))

    return res

- M April 22, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

def source_and_rotation(words: List[str]) -> List[List[str]]:

    def rot_char_by(c, i):
        shift = ord('A')
        return chr((((ord(c) - shift) + i) % N) + shift)
    
    N = len(string.ascii_lowercase)

    transformed_to_origins = defaultdict(set)

    for w in words:
        for i in range(1, N):
            transformed_to_origins["".join([rot_char_by(c, i) for c in w])].add(w)

    res = []
    in_result = set()

    for w in words:
        if w in transformed_to_origins:
            for t in transformed_to_origins[w]:
                if t != w and (w, t) not in in_result:
                    res.append([w, t])
                    in_result.add((w, t)), in_result.add((t, w))

    return res

- Anonymous April 22, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I think you can transform each string into an array of differences (FOO -> [F-O, O-O]), and then you will just have to use a data structure to find matching arrays ( a trie maybe ) or some smart hashes.

- Anonymous April 27, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Here is an idea - construct a prefix tree, where each node is character, child of a node exists if and only if there is a word in original collection that has these characters going one after another. E.g. for collection ['bar', 'baz', 'cat'], the tree would look like:

Tree:

        [z]
        /   
[b] -> [a] 
        \
        [r]

[c] -> [a] -> [t]

Now for each word in the collection, and for every rotation you can descend in the tree. If path exists - the rotation exists too. Note - it is also important to mark last symbol of a string as "terminal" in the tree, so that algorithm would not match "AB", "BCZ" as ["AB", BC'] -> C is not terminal.

Here is the full implementation in JS:

var alphabet = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ');
var charIndex = makeCharIndex(alphabet);

console.log(getRotationPairs(['AB', 'BC', 'BCD', 'FOO', 'ZA', 'BAZ']));

function getRotationPairs(wordCollection) {
  var prefixTree = makePrefixTree(wordCollection);
  var rotationPairs = [];
  
  wordCollection.forEach(word => {
    var foundPairs = getRotationPairsForWord(word, prefixTree);
    rotationPairs = rotationPairs.concat(foundPairs)
  });
  
  return rotationPairs;
}

function getRotationPairsForWord(word, prefixTree) {
  var foundPairs = [];
  
  for (var rotationNumber = 1; rotationNumber < alphabet.length; ++rotationNumber) {
    var foundRotation = findRotationInTree(rotationNumber, word, prefixTree);
    if (foundRotation) {
      foundPairs.push([word, foundRotation]);
    }
  }
  
  return foundPairs;
}

function findRotationInTree(rotationNumber, word, prefixTree) {
  var currentNode = prefixTree;
  var rotatedWord = '';
  for (var i = 0; i < word.length; ++i) {
    var char = rotate(word[i], rotationNumber);
    if (!currentNode[char]) return;
    rotatedWord += char;
    currentNode = currentNode[char];
  }

  if (currentNode.isTerminal) return rotatedWord;
}

function rotate(char, offset) {
  return alphabet[(charIndex[char] + offset) % alphabet.length];
}

function makePrefixTree(wordCollection) {
  var tree = Object.create(null);
  wordCollection.forEach(addWordToTree);
  return tree;
  
  function addWordToTree(word) {
    var currentNode = tree;
    for (var i = 0; i < word.length; ++i) {
      var char = word[i];
      var nextNode = currentNode[char];
      if (!nextNode) {
        nextNode = currentNode[char] = Object.create(null);
      }
      
      currentNode = nextNode;
    }
    
    if (word.length > 0) {
      // So that we can check if word truly ends here.
      currentNode.isTerminal = true;
    }
  }
}

function makeCharIndex(alphabet) {
  var charIndex = {};
  alphabet.forEach((char, index) => charIndex[char] = index);
  
  return charIndex;
}

Tree is constructed in O(m) time, where `m` is total number of characters in your collection. The lookup for rotation is also linear function of characters count, so the overall performance is `O(m)`, `m` - total number of characters, and a constant factor of rotation counts (length of your alphabet).

- anvaka May 01, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

public static void rotTrans(String [] list){
		
		HashMap<String,ArrayList<String>> map = new HashMap<String,ArrayList<String>>();
		String key;
		int len = 0,i = 0, j = 0;
		
		for(String s: list){
			key = keyGen(s);
			if(map.containsKey(key)){
				map.get(key).add(s);
			}else{
				map.put(key, new ArrayList<String>());
				map.get(key).add(s);
			}
		}
		for(String k : map.keySet())
			len += map.get(k).size() - 1;
		
		String [][] result = new String [len][2];
		
		for(Map.Entry<String,ArrayList<String>> entry : map.entrySet()){
			if(entry.getValue().size() > 1){
				for(j = 1; j < entry.getValue().size(); j++){
					result[i][0] = entry.getValue().get(0);
					result[i][1] = entry.getValue().get(j);
					i++;
				}
			}
		}
		System.out.println(Arrays.deepToString(result));
	}
	public static String keyGen(String str){
		if(str.length() <= 1) return "1";
		
		StringBuilder sb = new StringBuilder();
		char ch1,ch2;
		ch1 = str.charAt(0);
		sb.append(1);
		for(int i = 1; i < str.length(); i++){
			ch2 = str.charAt(i);
			if(ch2 >= ch1){
				sb.append((ch2 - ch1) + 1);
			}else{
				sb.append((ch1 - 'Z' + 1) + (('A' - ch2) + 1));
			}
			ch1 = ch2;
		}
		return sb.toString();
	}

- Raminder May 28, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

const normalize = string => string.split('').map(x => x.charCodeAt(0));
const transform = arr => arr.map(val => ((val -  arr[0]) + 26) % 26);
const normalizeString = string => transform( normalize(string)).join('');
const generateBuckets = strings => {
	const buckets = {  };
	const values = strings.forEach(string => {
		const normal = normalizeString(string);
		if (buckets[normal] === undefined){ buckets[normal] = [ ] };
		buckets[normal].push(string);
	});
	return buckets;
};

- Anonymous May 29, 2018 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I assume, to each word, we can apply any number of ROT_1 or any number of ROT_25.

#include <unordered_map>
#include <vector>
#include <string>
#include <iostream>

using namespace std;

pair<string, string> GetPatterns(const string& s)
{
    string p1, p2;
    for (int i = 0; i + 1 < s.size(); ++i)
    {
        int delta1 = (static_cast<int>(s[i + 1]) - s[i]);
        if (delta1 < 0)
        {
            delta1 += 26;
        }
        int delta2 = (static_cast<int>(s[i]) - s[i + 1]);
        if (delta2 < 0)
        {
            delta2 += 26;
        }
        p1 += to_string( delta1) + ",";
        p2 += to_string(-delta2) + ",";
    }
    return pair<string, string>(p1, p2);
};

vector<string> RotationDups(const vector<string>& words)
{
    vector<string> out;
    unordered_map<string, int> patterns;
    for (auto w : words)
    {
        pair<string, string> p = GetPatterns(w);
        ++patterns[p.first];
        ++patterns[p.second];
    }
    for (auto w : words)
    {
        pair<string, string> p = GetPatterns(w);
        if (patterns[p.first] > 1 ||
            patterns[p.second] > 1)
        {
            out.push_back(w);
        }
    }
    return out;
}


int main()
{
    vector<string> out = RotationDups({"AB", "BC", "FOO", "ZA", "BAZ"});
    for (auto w : out)
    {
        cout << w << ", ";
    }
    cout << "\n";
    return 0;
}

- Alex October 25, 2018 | Flag Reply


Add a Comment
Name:

Writing Code? Surround your code with {{{ and }}} to preserve whitespace.

Books

is a comprehensive book on getting a job at a top tech company, while focuses on dev interviews and does this for PMs.

Learn More

Videos

CareerCup's interview videos give you a real-life look at technical interviews. In these unscripted videos, watch how other candidates handle tough questions and how the interviewer thinks about their performance.

Learn More

Resume Review

Most engineers make critical mistakes on their resumes -- we can fix your resume with our custom resume review service. And, we use fellow engineers as our resume reviewers, so you can be sure that we "get" what you're saying.

Learn More

Mock Interviews

Our Mock Interviews will be conducted "in character" just like a real interview, and can focus on whatever topics you want. All our interviewers have worked for Microsoft, Google or Amazon, you know you'll get a true-to-life experience.

Learn More