Google Interview Question for Software Developers


Country: United States
Interview Type: Phone Interview




Comment hidden because of low score. Click to expand.
4
of 4 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.
0
of 0 votes

You don't need to find rotation n all. Just find index of both character
Say cik and cjk obtained at index i and j of kth item.
Index of the is i1 and i2.
If i2 < i1 then
Return 26-(i1-i2)
Otherwise
Return i1-i2

- nitinguptaiit April 16, 2019 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Complexity O(L* n^2)
L is max length of word.

- nitinguptaiit April 16, 2019 | Flag
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
Comment hidden because of low score. Click to expand.
0
of 0 vote

Here is some code;
Bruteforce and optimised;

package Java;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Author: Nitin Gupta(nitin.gupta@walmart.com)
 * Date: 16/04/19
 * Description:
 */
public class StringRotationMatch {

    static class Pair {
        String s1, s2;

        @Override
        public String toString() {
            return "{" + s1 + "," + s2 + '}';
        }

        public Pair(String s1, String s2) {
            this.s1 = s1;
            this.s2 = s2;
        }
    }

    public static void main(String args[]) {
        String[] arr1 = {"AB", "BC", "FOO", "ZA", "BAZ"};
        System.out.println(rotationPairsBruteForce(arr1));
        System.out.println(rotationPairs(arr1));

        String[] arr2 = {"AB", "BC", "FOO", "ZA", "BAZ", "CBA"};
        System.out.println(rotationPairsBruteForce(arr2));
        System.out.println(rotationPairs(arr2));
    }


    //O(Ln^2)
    private static List<Pair> rotationPairsBruteForce(String arr[]) {
        String str = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        List<Pair> out = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) { //O(n)
            for (int j = i + 1; j < arr.length; j++) { //O(n)
                if (i == j)
                    continue;

                String s1 = arr[i];
                String s2 = arr[j];

                if (s1.length() != s2.length())
                    continue;

                char c1 = s1.charAt(0);
                char c2 = s2.charAt(0);
                int diff = getDiff(c1, c2, str);
                int k;
                for (k = 1; k < s1.length(); k++) { //O(L)

                    if (diff != getDiff(s1.charAt(k), s2.charAt(k), str))
                        break;
                }

                if (k == s1.length()) {
                    out.add(new Pair(s1, s2));
                }

            }
        }
        return out;
    }

    private static int getDiff(char c1, char c2, String str) { //O(1)

        int index1 = str.indexOf(c1);
        int index2 = str.indexOf(c2);

        if (index2 < index1)
            return 26 - (index1 - index2);
        return index2 - index1;
    }

    //Optimized-> O(nL)
    private static List<Pair> rotationPairs(String[] arr) {

        List<Pair> out = new ArrayList<>();
        List<String> transformed = transform(arr); //O(nL)

        Map<String, List<Integer>> duplicates = new HashMap<>();

        int i = 0;
        //This loop will run at most O(n) time since in inner loop even all of them map to one entry only, then each element will touch at most 2 times
        for (String s : transformed) { //O(n)

            List<Integer> entries = duplicates.getOrDefault(s, new ArrayList<>());


            for (Integer entry : entries) {

                out.add(new Pair(arr[entry], arr[i]));
            }
            entries.add(i);
            duplicates.put(s, entries);
            i++;
        }


        return out;
    }

    //O(nL)
    private static List<String> transform(String ar[]) {
        List<String> transformed = new ArrayList<>();

        for (int i = 0; i < ar.length; i++) { //O(n)

            String x = ar[i];

            int delta = 'A' - x.charAt(0);

            StringBuilder str = new StringBuilder();
            for (int j = 0; j < x.length(); j++) { //O(L)
                char current = x.charAt(j);

                current = (char) ((int) current + delta);

                if (current < 'A')
                    current = (char) ((int) current + 26);

                str.append(current);
            }
            transformed.add(str.toString());
        }

        return transformed;

    }
}

- nitinguptaiit April 16, 2019 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Rotate every string to start from 'a' and map keys to value as list of strings. if list has more than one string than add that to resulting set.

public List<List<String>> similarStrings(List<String> stringList){
		if(stringList.size() == 0)return null;
		List<List<String>> sameStrings = new ArrayList<>();
		Map<String, List<String>> map = new HashMap<>();
		
		for(String str : stringList){
			if(str.length() > 0){
				String rotatedString = rotateString(str);
				map.putIfAbsent(rotatedString, new ArrayList<String>());
				map.get(rotatedString).add(str);
			}
		}
		for(Map.Entry<String, List<String>> e : map.entrySet()){
			if(e.getValue().size() > 1){
				sameStrings.add(e.getValue());
			}
		}
		return sameStrings;
	}
	private String rotateString(String str){
		if(str.length() == 0)return str;
		StringBuilder sb = new StringBuilder();
		str = str.toLowerCase();
		char lastChar = 'a';
		
		sb.append('a');
		for(int i = 1; i < str.length();i++){
			char curr = str.charAt(i);
			char prev = str.charAt(i - 1);
			char rotatedChar = 0;
			if(curr > prev){
				int delta = curr - prev;
				rotatedChar = (char) (lastChar + delta);
			}else if(curr == prev){
				rotatedChar = lastChar;
			}else if(curr < prev){
				int delta = prev - curr;
				if(lastChar - delta >= 'a'){
					rotatedChar = (char) (lastChar - delta);
				}else{
					delta = 'a' - delta;
					rotatedChar = (char) (('z' - delta) + 1);
				}
			}
			sb.append(rotatedChar);
			lastChar = rotatedChar;
		}
		return sb.toString();
	}

- Raminder March 09, 2020 | 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