Facebook Interview Question for Software Developers


Country: United States




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

1. I need to find subsets that sum up to an element in the array, that is actually itself an NP complete problem. So I run this problem n times (for each element in the array). But it's worse, because I need the actual subsets which can be many (Teta(n!) .. e.g. assume 0,1,-1,1-1,1,-1,1,-1)
2. of all this subsets I need to find out what the set of disjoint subsets is that has the most elements.

difficult, any hints, ideas?

- Chris September 07, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
1
of 1 vote

@chrisk, I could be wrong or else some else has similar but robust logic above code snippets submitted, can you please let me know if you think this will work
1) find max negative number and offset with his value so that all numbers are positive or zero
2) Sort the numbers
2) iterate i from 1 to n-1
perform perfect sum problem with (0, i-1) with sum = nums[i] , using dp this costs O(n * sum) to calculate subsets, (If there are duplicate sums, I am not sure if first one or longest one to pick to mark as removed along with ith index, may be this decision may result in np complete like you said)

the numbers that remaining, count is the answer, it will take it O(n^2 * sum)?

- tryingtolearn September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
1
of 1 vote

@trying to learn: indeed there is a dp solution to calculate subsets that sum up to a target value (a pseudo polynomial solution). One can modify it for this problem, one can even get all subsets for a given target, but at this point you need to choose one or none of this subsets and repeat for all choices and all following choices to build the min. It's codeable, certainly, but it's ugly, more efficient then the other solutions here, but still not nice. There might be a more elegant step from the sum-problem to deciding which subsets to use, but I haven't really found that. It looks like a contest question, so there might be some hacky dp that works with special conditions, which are not really given here (maybe elements are unique, etc..)

btw. for the perfect sum dp, you do not need to sort - as for knapsack, no soting is required, because the question is only is the element in or not and basically what you do is iterate over the range of the desired sum and mark all sums that you can reach with (and without from previous iterations) that element. How ever, sort doesn't hurt, because it's an order of magnitude faster than the actual DP ;-)

- Chris September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

public List<Integer> findAll(List<Integer> nums) {

        // the list should contain at least 3 elements
        if(nums.size() < 3){
            return  nums;
        }


        Stack<Integer> stack = new Stack<>();
        // sort in descending order (actually it can be sorted only first time the list sent,
        // so wrapping it with another method will do the job)
        nums = nums.stream().sorted((i1, i2) -> Integer.compare(i2, i1)).collect(Collectors.toList());

        // pick the maximum number from the list
        int curSum = nums.get(0);

        // put the second number in the list to the top of the stack and set the loop to start from the 3rd element
        int lastIndex = 2;
        stack.add(nums.get(1));

        // loop until all the permutations tested
        while (!stack.isEmpty()) {
            // loop till the end of the list starting from the last element added to the stack in the previous round
            for (int ind = lastIndex; ind < nums.size(); ind++) {
                // in case the difference > 0 you can subtract another number
                if(curSum  - stack.peek() > 0){
                    curSum-=stack.peek();
                // in case it equals 0, all the elements in the stack added will equal to first element in the list
                }else if(curSum  - stack.peek() == 0){
                    // remove all the elements in the stack and the 1st element
                    nums.removeAll(stack);
                    nums.remove(nums.get(0));
                    return findAll(nums);
                // lesser than zero means the last number you tried can not be a part of the expression
                }else{
                    stack.pop();
                }
                // add next number ot stack  and update the last index
                stack.add(nums.get(ind));
                lastIndex = ind;
            }
        }

        return nums;

}

- igor.iris September 07, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

At first glance, the solution seems simple (when assuming: no duplicate numbers in the array). Even when adding duplicate entries, this is still solvable with not much of change.

The idea here is to visit each permutation (per target number) only once. We keep on recursing on the array until we find the lowest result.

Running the example numbers will yield 5 possible solutions, from which we pick the least one (which is one)

std::unordered_set<int> all_numbers;
std::unordered_set<std::string> visited_matches;

std::string make_key(const std::vector<int>& numbers, int target)
{
    std::string key;
    for(int i = 0; i < (int)numbers.size(); ++i) {
        key += std::to_string(numbers[i]);
    }
    key += std::to_string(target);
    return key;
}

bool is_equal(const std::vector<int>& numbers, int target)
{
    int res = 0;
    std::string k = make_key(numbers, target);
    
    if(visited_matches.count(k)) {
        return false;
    }
    visited_matches.insert(k);
    for(int i = 0; i < (int)numbers.size(); ++i) {
        res += numbers[i];
    }

    if(res == target) {
        // remove these numbers from the set
        for(int i = 0; i < (int)numbers.size(); ++i) {
            std::cout << numbers[i] << " ";
            all_numbers.erase(numbers[i]);
        }
        std::cout << " => " << target << std::endl;
        all_numbers.erase(target);
        return true;
    } else if(res < target) {
        return false;
    }

    // recurse
    for(int i = 0; i < (int)numbers.size(); ++i) {
        std::vector<int> v = numbers;
        v.erase(v.begin() + i); // remove the i-th entry to create a new subset and try again
        if(is_equal(v, target)) {
            return true;
        }
    }
    return false;
}

int search_subset_groups(const std::vector<int>& numbers)
{
    std::cout << "--------------------------------------------" << std::endl;
    
    // initialize the hash
    all_numbers.clear();
    all_numbers.insert(numbers.begin(), numbers.end());

    bool cont = false;
    while(true) {
        // create the list of numbers to work on
        // We copy it from the hash
        std::vector<int> numbersLeft;
        std::for_each(all_numbers.begin(), all_numbers.end(), [&](int n) { numbersLeft.push_back(n); });

        // sort the list in desc order
        std::sort(numbersLeft.begin(), numbersLeft.end(), [](int a, int b) { return a > b; });

        // And try to find matches
        for(int i = 0; i < (int)numbersLeft.size(); ++i) {
            std::vector<int> v = numbersLeft;
            int target = numbersLeft[i];
            v.erase(v.begin() + i);
            if(is_equal(v, target)) {
                cont = true;
                break;
            }
        }

        if(cont) {
            cont = false;
            continue;
        }
        break;
    }
    
    std::cout << "==> we are left with array of size " << all_numbers.size() << std::endl;
    std::for_each(all_numbers.begin(), all_numbers.end(), [&](int n) { std::cout << "    " << n << std::endl; });
    return all_numbers.size();
}

int getNumbers(const std::vector<int>& numbers)
{
    int size = search_subset_groups(numbers);
    if(size == 0) return 0;
    while(size < (int)numbers.size()) {
        int newIterSize = search_subset_groups(numbers);
        if(newIterSize == (int)numbers.size()) {
            break;
        }
        if(newIterSize < size) size = newIterSize;
        if(newIterSize == size) continue;
    }
    return size;
}

///------------------------------------------------------
/// Main
///------------------------------------------------------
int main(int argc, char** argv)
{
    std::vector<int> numbers = { 48, 20, 1, 3, 4, 6, 9, 24 };
    // std::sort(numbers.begin(), numbers.end(), [](int a, int b) { return a > b; });
    int minArrSize = getNumbers(numbers);
    std::cout << "The best match leaving us with an array of size: " << minArrSize << std::endl;
    return 0;
}

- PenChief September 07, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

@chrisK, as always thanks for your detailed response :), I was also concerned about which subset to chose, the problem does not say minimization but being FB interview question it may be implied like you suggested. The only reason I sorted because, we don't have to consider elements greater than index i, for subset problem and you are right, it won't improve that much but it won't hurt either.

- tryingtolearn September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

@tryingtolearn: an other thing, the offset is tempting but you need to take into account the following:
if you create a sum over k element, and each element is original value + offset, you have k offsets in the sum. if a+b+c=d, then sum(a+o,b+o,c+o) = d+3*o, but you would offset d only by one o (as it is in the array). For the dp to work, you either assume positive numbers or, create a dp range into the negative and positive (which actually should be possible)

- Chris September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

@chrisk thanks for pointing out the problems with offset approach, I did not take into account at all, I would have to calculate carefully the sum, or consider mapping of ranges for dp as you suggested thanks again

- tryingtolearn September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

import b.k.P;

import java.util.*;
public class Main {

    public static void main(String[] args) {

        int[]n = new int []{1,2,3,5,6,11};
        boolean[] used =new boolean[n.length];
        int r = solve(n.length,n,used);

    }

    static int solve(int remain, int[] nums, boolean[] used){
        int res = remain;
        if(remain<3){
            return remain;
        }
        for(int i=0;i<nums.length;i++){
            if(!used[i]){
                //try it as sum-> get all subsets
                used[i]=true;
                remain--;
                List<List<Integer>> subsets = new ArrayList<List<Integer>>();
                getsets(nums[i],nums,used,new ArrayList(),subsets,0);
                if(subsets.size()>0){
                    for(List<Integer> l :subsets){
                        for(Integer j:l){
                            used[j]=true;
                            remain--;
                        }
                        res = Math.min(solve(remain,nums,used),res);
                        for(Integer j:l){
                            used[j]=false;
                            remain++;
                        }
                    }
                }
                used[i]=false;
                remain++;
            }
        }
        return res;
    }

    static void getsets(int target,int[]nums,boolean[]used,ArrayList<Integer> temp,List<List<Integer>> res,int idx){
        if(target==0){
            res.add(new ArrayList<Integer>(temp));
            return;
        }

        for(int i=idx;i<nums.length;i++){
            if(used[i]||target-nums[i]<0){continue;}
            used[i]=true;
            target-=nums[i];
            temp.add(i);
            getsets(target,nums,used,temp,res,i+1);
            temp.remove(temp.get(temp.size()-1));
            used[i]=false;
            target+=nums[i];
        }
    }
}

- alex.climov September 08, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

I wrote another solution based on my previous solution. A bit more elegant (this time using queue (vector based)

The code:

#include <memory>
#include <vector>
#include <list>
#include <iostream>
#include <string.h>
#include <iomanip>
#include <math.h>
#include <unordered_map>
#include <unordered_set>
#include <stdlib.h>
#include <algorithm>
#include <queue>
#include <vector>
#include "PriorityQueue.h"

std::unordered_set<int> Numbers;
std::vector<int> TargetNumbers;
std::unordered_set<std::string> Visited;
std::vector<std::vector<int> > Matches;

// Create a unique key to identify this combination (numbers + their target value)
std::string makeKey(const std::vector<int>& numbers, int targetNumber)
{
    std::string s;
    std::for_each(numbers.begin(), numbers.end(), [&](int n) { s += std::to_string(n); });
    s += std::to_string(targetNumber);
    return s;
}

bool find_subsets(const std::vector<int>& numbers, int targetNumber)
{
    // assume: numbers is sorted in desc order
    std::string k = makeKey(numbers, targetNumber);
    if(Visited.count(k)) return false;
    
    // Calc the sum
    int sum = 0;
    std::for_each(numbers.begin(), numbers.end(), [&](int n) { sum += n; });

    if(sum == targetNumber) {
        // Remove this array from the global array
        std::vector<int> matchedNumbers = numbers;
        matchedNumbers.push_back(targetNumber);
        Matches.push_back(matchedNumbers);
        
        std::for_each(matchedNumbers.begin(), matchedNumbers.end(), [&](int n) { Numbers.erase(n); });
        Visited.insert(k);
        return true;
    }

    if(sum < targetNumber) return false;

    // Try other permutations
    for(size_t i = 0; i < numbers.size(); ++i) {
        std::vector<int> v = numbers;
        v.erase(v.begin() + i);
        if(find_subsets(v, targetNumber)) {
            return true;
        }
    }
    return false;
}

int get_min_array_size(const std::vector<int>& numbers)
{
    //Visited.clear();
    Numbers.clear();
    TargetNumbers.clear();
    Matches.clear();
    
    // Initialise the Numbers set
    TargetNumbers = numbers;
    std::for_each(numbers.begin(), numbers.end(), [&](int n) { Numbers.insert(n); });
    std::sort(TargetNumbers.begin(), TargetNumbers.end(), [](int a, int b) { return a > b; });
    
    while(!TargetNumbers.empty()) {
        // Pop the first number to try
        int targetNumber = TargetNumbers[0];
        // Always remove the number from the queue
        TargetNumbers.erase(TargetNumbers.begin());
        if(Numbers.count(targetNumber) == 0) {
            // this number was already processed
            continue;
        }

        // Create an array of numbers from the remaining numbers and sort it in desc order
        std::vector<int> arr;
        std::for_each(Numbers.begin(), Numbers.end(), [&](int n) {
            if(n != targetNumber) {
                arr.push_back(n);
            }
        });
        std::sort(arr.begin(), arr.end(), [](int a, int b) { return a > b; });

        // Process all possibilities
        find_subsets(arr, targetNumber);
    }
    return Numbers.size();
}

int main(int argc, char** argv)
{
    // Print the path
    std::vector<std::vector<int> > BestMatches;
    std::vector<int> numbers = { 48, 20, 1, 3, 4, 6, 9, 24 };
    int minSize = (int)numbers.size();
    int arrSize = 0;
    while(arrSize != (int)numbers.size()) {
        arrSize = get_min_array_size(numbers);
        if(arrSize < minSize) {
            BestMatches.swap(Matches);
            minSize = arrSize;
        }
    }
    
    // Print the groups
    std::cout << "We are left with an array of size: " << minSize << std::endl;
    std::cout << "Removed the following groups:" << std::endl;
    std::for_each(BestMatches.begin(), BestMatches.end(), [&](const std::vector<int>& v){
        std::cout << "[";
        std::for_each(v.begin(), v.end(), [](int n){
            std::cout << n << " ";
        });
        std::cout << "]" << std::endl;
    });
    return 0;
}

- PenChief September 10, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

This is a fast solution in python also (not the optimal one though):

def sum1(list_):
    ord_list = sorted(list_,reverse=True)
    all_values = []
    i=0
    while i < len(ord_list):
        max_ = ord_list[i]
        a = [max_]
        for k in range(i+1,len(ord_list)):
            if max_ - ord_list[k] > 0:
                a.append(ord_list[k])
                max_ = max_ - ord_list[k]
            elif max_ - ord_list[k] == 0:
                a.append(ord_list[k])
                all_values.append(a)
                for m in a:
                    ord_list.remove(m)
                break
        if k == len(ord_list)-1:
            i = i + 1
    return ord_list, all_values

- Jona September 10, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Special case of subset sum
1.Sort the array, say descending order
2.A[i] >= A[j] for all i<j<N (SUBSET SUM PROBLEM)
3.For each A[i] find subset in j=i+1..N that adds to A[i]
4.For all elements in A eliminated in step 3 mark them as -1 assuming array is of natural numbers
5.Run 2..4 for the entire scan.

Time complexity nlogn + n^2
Space complexity = n if modification of original array is not allowed.

What do you guys think of this approach?

- Swanand Rao September 14, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

int min_left_after_sum_removal_aux(const std::multiset<int>& s, int sum) {
	int min = std::numeric_limits<int>::max();
	for (auto& i = s.rbegin(); i != s.rend(); ++i) {
		if (sum - *i > 0) {
			std::multiset<int> new_s(s);
			new_s.erase(new_s.lower_bound(*i));
			min = std::min(min, min_left_after_sum_removal_aux(new_s, sum - *i));
		}
		else if (sum - *i == 0) {
			std::multiset<int> new_s(s);
			new_s.erase(new_s.lower_bound(*i));
			min = std::min(min, min_left_after_sum_removal(new_s));
		}
	}
	return min;
}

int min_left_after_sum_removal(const std::multiset<int>& s) {
	if (s.size() == 0) {
		return 0;
	}
	std::multiset<int> new_s(s);
	int sum = *new_s.rbegin();
	new_s.erase(new_s.lower_bound(sum));
	int v1 = 1 + min_left_after_sum_removal(new_s);
	int v2 = min_left_after_sum_removal_aux(new_s, sum);
	return std::min(v1, v2);
}

int min_left_after_sum_removal(const std::vector<int>& v) {
	std::multiset<int> sorted(v.begin(), v.end());
	return min_left_after_sum_removal(sorted);
}

- Omri.Bashari May 10, 2021 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

public static void main(String[] args) {

		int[] arr = { 48, 20, 1, 3, 4, 6, 9, 24 };
		sort(arr, 0, arr.length - 1);

		int l = left(arr);
		System.out.println(l);
	}

	public static int left(int[] nums) {
		int n = nums.length - 1;
		int count = 0;

		for (int i = n; i >= 0; i--) {
			if(nums[i] > -1){
				List<Integer> indexes = new ArrayList<Integer>();
				left(nums, nums[i], i-1, indexes, false);
				if (indexes.size() > 0) {
					nums[i] = -1;
					for (Integer ind : indexes) {
						nums[ind] = -1;
					}
				}
			}
		}
		
		for (int i = 0; i < nums.length; i++) {
			if(nums[i] > -1)
				count++;
		}
		
		return count;
	}

	public static boolean left(int[] nums, int sum, int p, List<Integer> indexes, boolean found) {
		if (sum < 0)
			return found;

		if (sum == 0)
			return true;

		if (p < 0)
			return found;
		
		if (!found && nums[p] != -1) {
			indexes.add(p);
			found = left(nums, sum - nums[p], p - 1, indexes, found);
			if (!found) {
				indexes.remove(new Integer(p));
				found = left(nums, sum, p - 1, indexes, found);
			}
		}else if (!found) {
			found = left(nums, sum, p - 1, indexes, found);
		}
		return found;
	}

	public static void sort(int[] arr, int l, int h) {

		int i = l;
		int j = h;
		int mid = arr[(h - l) / 2 + l];

		while (i <= j) {
			while (arr[i] < mid)
				i++;
			while (arr[j] > mid)
				j--;

			if (i <= j) {
				swap(arr, i, j);
				i++;
				j--;
			}
		}
		if (l < j)
			sort(arr, l, j);
		if (i < h)
			sort(arr, i, h);
	}

	public static void swap(int[] arr, int i, int j) {
		int t = arr[i];
		arr[i] = arr[j];
		arr[j] = t;
	}

- sudip.innovates September 25, 2017 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

@stupid.innovates: Why are you posting rubbish answers everywhere? Pls understand the question and then write.

- sri November 26, 2017 | Flag


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