Google Interview Question: Product of other Elements in an Array in O(n)

Last time I was interviewed for a software development engineer position, the recruiter asked me some of the classical Microsoft interview questions, such as “How Would You Move Mount Fuji?” or “How many gas station are there in your country?“.

It was the first time for me to be asked such questions but having obtained the job I think my answers were good enough.

After that day, I looked for other well-known interview questions and I discovered that Google has a totally different approach, focusing on algorithms, data structures and complexity.

For instance, one of Google interview questions says:

There is an array A[N] of N integers. You have to compose an array Output[N+1] such that Output[i] will be equal to the product of all the elements of A[] except A[i].

Example:
INPUT:[4, 3, 2, 1, 2]
OUTPUT:[12, 16, 24, 48, 24]

Solve it without division operator and in O(n).

Without the two constraints at the end of the puzzle it would have been straightforward, but now we have to be smart.

Actually, the product of all elements of A[] except A[i] is equal to the product of all elements before A[i] and those after A[i].

We can traverse A[] twice, once from left to right and once in the opposite way and multiply all the elements we find before A[i].

We’ll pretend to have a new array called Output[] to store the output of the first pass, assigning Output[i] the product of all elements preceding A[i]:

let rec firstpass product input =
    match input with
    | [] -> []
    | x::xs -> product :: firstpass (product * x) xs

For the second pass we need to move from right to left, but this can be done by reversing the input arrays and moving as usual:

let secondpass product input arr =
    let rev_input = List.rev input
    let rev_arr = List.rev arr
    let rec rev_secondpass product (input:list<int>) arr =
      match arr with
      | [] -> []
      | x::xs -> rev_secondpass (product * input.Head) input.Tail xs @ [(x * product)]

    rev_secondpass product rev_input rev_arr

Both firstpass and secondpass expect an integer argument called product, which will be always be 1 at the beginning and will be used to store the intermediate products during the recursive calls.

With these functions we can just define an input array and apply them to get the result.

The following is the complete F# code:

#light

let input = [ 4; 3; 2; 1; 2 ]

let answer values = 

  let rec firstpass product input =
    match input with
    | [] -> []
    | x::xs -> product :: firstpass (product * x) xs

  let secondpass product input arr =
    let rev_input = List.rev input
    let rev_arr = List.rev arr
    let rec rev_secondpass product (input:list<int>) arr =
      match arr with
      | [] -> []
      | x::xs -> rev_secondpass (product * input.Head) input.Tail xs @ [(x * product)]

    rev_secondpass product rev_input rev_arr

  values |> firstpass 1 |> secondpass 1 values

11 Comments »

claudio on June 10th 2008 in Functional programming

11 Responses to “Google Interview Question: Product of other Elements in an Array in O(n)”

  1. a. responded on 10 Jun 2008 at 7:37 pm #

    Microsoft focus on the same thing, they have problem solving and coding questions. But they start with the logical ones on phone interviews.

  2. claudio responded on 10 Jun 2008 at 7:43 pm #

    I’m glad to hear that, in my opinion a good mix of problem solving and coding questions is the best way to interview people for software development positions.

  3. OJ’s rants » An Interesting Little Problem responded on 14 Jun 2008 at 11:40 am #

    [...] post was inspired by a recent interview question that was posted over at fsharp.it. It’s one of those neat little questions which looks really simple on the surface but is [...]

  4. Jon Harrop responded on 26 Jun 2008 at 11:37 pm #

    As a dynamic programming problem you can solve this for small arrays using naive recursion wrapped in a memoize combinator:

    let product (a : _ array) =
        let rec f = memoize(fun (i, di) -&gt;
            if i = a.Length then 1 else f(i+di, di) * a.[i])
        Array.init a.Length (fun i -&gt; f(i-1, -1) * f(i+1, 1))
    
  5. claudio responded on 26 Jun 2008 at 11:47 pm #

    Only 4 lines instead of my 22, that’s a great solution!

  6. Pavel responded on 11 Jul 2008 at 3:30 pm #

    Hi claudio,

    Perhaps I’m wrong, but, IMHO, your solution has O(n^2) or O(n*log(n)) complexity because you use @ operator into recursive function.
    @ operator has O(n) complexity itself.

    BTW my solution http://fsharpere.blogspot.com/2008/07/google-interview-question.html

  7. claudio responded on 11 Jul 2008 at 4:11 pm #

    Hi Pavel,
    we should check how the concatenation operator @ is implemented, I’m not sure its complexity is O(n).
    BTW, your solution is very good!

  8. Problem with an interesting little problem « Antimatroid’s Weblog responded on 08 Aug 2008 at 2:18 am #

    [...] fsharp.it posted the above simple Google interview question on their site a few weeks ago and subsequently the problem was referenced by OJ’s rants and later posted to the programming subreddit on reddit.com. Like everyone else, I sat down and wrote some quick code to solve the problem. After looking a the solution, it seemed to me that this was a rather poorly conceived problem- if not contrived at heart. It doen’t ring to the tune of scalability, performance and quality that one thinks of when the word Google is thrown into the mix. [...]

  9. jhon responded on 25 Jun 2009 at 7:30 am #

    Hi,
    I interviewed for the associate product manager position at google, you can read it up here

    http://ferozeh.com/Interviews/Google/google.php

  10. claudio responded on 25 Jun 2009 at 8:01 am #

    @jhon: your interview reports are very interesting, thanks!

  11. Pravinth responded on 22 Jan 2011 at 12:10 pm #

    really a nice+good question!

Trackback URI | Comments RSS

Leave a Reply