Google Interview Question
Software Engineer / DevelopersCountry: United States
Interview Type: Phone Interview
I suppose this solution is correct: ie. do simple bruteforce
approach (recursive), and count the number of runs
in each step. We do not need to explore the paths where the # of runs is greater than M.
here is the code, please correct me if I am wrong:
int total_count = 0;
int N = 5, M = 2, K = 6, L = 1;
std::vector< int > seq;
// n_placed: number of digits in a sequence being placed
// n_last: last digit placed (for comparison)
// n_runs: # of runs counted so far
// dir: current direction: decreasing(0), increasing(1) or undefined(-1)
void count_sequences(int n_placed, int n_last, int n_runs, int dir) {
seq.push_back(n_last);
if(n_placed == N) {
if(n_runs == M) {
total_count++;
printf("%d: ", total_count);
for(typeof(seq.begin()) i = seq.begin(); i != seq.end(); i++) {
printf("%d ", *i);
}
printf("\n");
}
seq.pop_back();
return;
}
for(int i = 1; i <= K; i++) {
int diff = i - n_last;
// difference between adjacent elements is too large
if(ABS(diff) > L)
continue;
if(diff == 0) { // duplicate element: break the current sequence
// have enough runs: no need to explore this path
if(n_runs == M)
continue;
count_sequences(n_placed+1, i, n_runs+1, -1);
} else if(diff < 0) {
if(dir == -1) { // start a new decreasing sequence
count_sequences(n_placed+1, i, n_runs, 0);
// current sequence is decreasing: continue this sequence
} else if(dir == 0) {
count_sequences(n_placed+1, i, n_runs, 0);
// current sequence is increasing: break it
} else { // dir == 1
if(n_runs == M) // have enough runs already
continue;
count_sequences(n_placed+1, i, n_runs+1, 0);
}
} else { // diff > 0
if(dir == -1) { // start a new increasing sequence
count_sequences(n_placed+1, i, n_runs, 1);
// current sequence is increasing: continue this sequence
} else if(dir == 1) {
count_sequences(n_placed+1, i, n_runs, 1);
// current sequence is decreasing: break it
} else { // dir == 0
if(n_runs == M) // have enough runs already
continue;
count_sequences(n_placed+1, i, n_runs+1, 1);
}
}
}
seq.pop_back(); // pop the current element
}
int main() {
for(int i = 1; i <= K; i++) {
count_sequences(1, i, 1, -1);
}
return 1;
}
e.g., for N = 5, M = 2, K = 6, L = 1 we get:
1: 1 1 2 3 4
2: 1 2 2 3 4
3: 1 2 3 2 1
4: 1 2 3 3 2
5: 1 2 3 3 4
6: 1 2 3 4 3
7: 1 2 3 4 4
8: 2 1 1 2 3
9: 2 1 2 3 4
10: 2 2 3 4 5
11: 2 3 3 2 1
12: 2 3 3 4 5
13: 2 3 4 3 2
14: 2 3 4 4 3
15: 2 3 4 4 5
16: 2 3 4 5 4
17: 2 3 4 5 5
18: 3 2 1 1 2
19: 3 2 1 2 3
20: 3 2 2 3 4
21: 3 2 3 4 5
22: 3 3 4 5 6
23: 3 4 3 2 1
24: 3 4 4 3 2
25: 3 4 4 5 6
26: 3 4 5 4 3
27: 3 4 5 5 4
28: 3 4 5 5 6
29: 3 4 5 6 5
30: 3 4 5 6 6
31: 4 3 2 1 1
32: 4 3 2 1 2
33: 4 3 2 2 1
34: 4 3 2 2 3
35: 4 3 2 3 4
36: 4 3 3 2 1
37: 4 3 3 4 5
38: 4 3 4 5 6
39: 4 4 3 2 1
40: 4 5 4 3 2
41: 4 5 5 4 3
42: 4 5 6 5 4
43: 4 5 6 6 5
44: 5 4 3 2 2
45: 5 4 3 2 3
46: 5 4 3 3 2
47: 5 4 3 3 4
48: 5 4 3 4 5
49: 5 4 4 3 2
50: 5 4 4 5 6
51: 5 5 4 3 2
52: 5 6 5 4 3
53: 5 6 6 5 4
54: 6 5 4 3 3
55: 6 5 4 3 4
56: 6 5 4 4 3
57: 6 5 4 4 5
58: 6 5 4 5 6
59: 6 5 5 4 3
60: 6 6 5 4 3
lets take an array A[1...P] (P > N).
We make an array with the runs arr[1.. P] R where all the elements from single run (except the last one which goes in the next sequence) are colored the same (have same number).
(All the sub arrays bellow are marked just with indexes)
Follow L Rule: So lets split the array to a sub arrays that follow the L rules. We make an help array B[1..P](I used it to build R array) which we fill with the difference between the current and previous elements (B[i] = A[i] - A[i - 1]) and we split the array on the point where abs(B[i]) > L. Now we have sub arrays that follow the L rule.
Follow the K rule: here we just split the sub arrays from above(after L rule) on the points where A[i] > K;
Follow the N and M rule: here we iterate all possible sub arrays with length = N and count the color change in R to be equal to M.
If tall the rules are true we count++;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class GoogleSequence {
class Seq {
private LinkedList<LinkedList<Integer>> sets;
public Seq(LinkedList<LinkedList<Integer>> sets) {
this.sets = sets;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return sets.toString();
}
}
public LinkedList<Seq> calculate(int N, int M,
int K, int L) {
// pos seq of N
// with M runs
// nums in seq <= K
// adj diff <=L
LinkedList<LinkedList<Integer>> lists = new LinkedList<LinkedList<Integer>>();
for (int j = 0; j < N; j++) {
int size = lists.size();
if (size == 0) {
for (int i = 1; i <= K; i++) {
LinkedList<Integer> newL = new LinkedList<Integer>();
newL.add(i);
lists.add(newL);
}
}
LinkedList<Integer> list = null;
for (int s = 0; s < size; s++) {
list = lists.get(s);
LinkedList<Integer> newL = new LinkedList<Integer>(list);
for (int i = 1; i <= K; i++) {
LinkedList<Integer> temp = new LinkedList<Integer>();
if (i == 1) {
list.offerLast(i);
} else {
temp.addAll(newL);
temp.offerLast(i);
lists.offerLast(temp);
}
}
}
}
LinkedList<Integer> pk = lists.peekLast();
LinkedList<Seq> finalLists = new LinkedList<Seq>();
for (LinkedList<Integer> list : lists) {
int index = 0;
LinkedList<LinkedList<Integer>> sets = new LinkedList<LinkedList<Integer>>();
while (list.size() > 0 && (index+1)<list.size()) {
int prev = list.get(index);
int next = list.get(index+1);
if (prev + L != next && prev - L != next) {
List<Integer> sub = list.subList(0, index+1);
sets.add(new LinkedList<Integer>(sub));
if (list.size() >= index){
list = new LinkedList<Integer>(list.subList(index+1,
list.size()));
index = 0;
}
}else{
index++;
if (index+1==list.size()){
sets.add(list);
}
}
}
if (sets.size() == M) {
finalLists.add(new Seq(sets));
}
}
return finalLists;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
GoogleSequence seq = new GoogleSequence();
System.out.println("Type four numbers, N M K L");
LinkedList<Seq> lists = seq.calculate(sc.nextInt(),
sc.nextInt(), sc.nextInt(), sc.nextInt());
System.out.println("POSSIBLE:" + lists.size());
for (Seq linkedList : lists) {
System.out.println(linkedList.toString());
}
}
}
I got one of the result as: [[9], [5], [7, 4, 7]]
Is this a valid result? If yes, i think i misunderstood the question. Can you explain why it's a valid seq?
Well from what I understood the question wanted you to find all sequence of numbers that met the four criteria. For example N=5, M=3, K=9,L=1. N stands for the length of the sequences, therefore here each sequence has 5 elements. What I did was calculate all combinations of sequences 11111,11112,11113.... up to K,K,K,K,K . K stands for the maximum number for each element. After finding all combinations I split each one to sets where each set would be 1+L, 1+L+L.... or 5-L,5-L-L.. Here L is 1 so sets for this sequence (1,2,3,4,5,8,7,6,4,3,2,1) this sequence is more than 5 but just to explain it. Sets would be 1,2,3,4,5,8 8,7,6 4,3,2,1, So we have 3 sets for this sequence. M is 3 so it would be a match. M stands for the number of sets in each sequence. :)
I have tweak the code a bit
package com.algorithms;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class GoogleSequence {
public class Seq {
private LinkedList<LinkedList<Integer>> sets;
public LinkedList<LinkedList<Integer>> getSets() {
return sets;
}
public Seq(LinkedList<LinkedList<Integer>> sets) {
this.sets = sets;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return sets.toString();
}
}
public LinkedList<Seq> calculate(int N, int M, int K, int L) {
// pos seq of N
// with M runs
// nums in seq <= K
// adj diff <=L
LinkedList<LinkedList<Integer>> lists = new LinkedList<LinkedList<Integer>>();
// Get all combinations of sequnces up to N
for (int j = 0; j < N; j++) {
int size = lists.size();
// If lists is empty add the first up to K numbers
if (size == 0) {
for (int i = 1; i <= K; i++) {
LinkedList<Integer> newL = new LinkedList<Integer>();
newL.add(i);
lists.add(newL);
}
}
LinkedList<Integer> list = null;
// Iterate up to the current size of the list.
// Add up to K to the end of each sublist
// The iterations follow this pattern,
// 1,2,3,4,... K
// 11,21,31,41,12,22,...,13,23.....K1
for (int s = 0; s < size; s++) {
list = lists.get(s);
LinkedList<Integer> newL = new LinkedList<Integer>(list);
for (int i = 1; i <= K; i++) {
LinkedList<Integer> temp = new LinkedList<Integer>();
if (i == 1) {
list.offerLast(i);
} else {
temp.addAll(newL);
temp.offerLast(i);
lists.offerLast(temp);
}
}
}
}
LinkedList<Integer> pk = lists.peekLast();
LinkedList<Seq> finalLists = new LinkedList<Seq>();
for (LinkedList<Integer> list : lists) {
int index = 0;
LinkedList<LinkedList<Integer>> sets = new LinkedList<LinkedList<Integer>>();
while (list.size() > 0 && (index + 1) < list.size()) {
int prev = list.get(index);
int next = list.get(index + 1);
//Check if pattern exists , if it doesnt then add the new set
if (prev + L != next && prev - L != next) {
List<Integer> sub = list.subList(0, index + 2);
sets.add(new LinkedList<Integer>(sub));
if (list.size() >= index+1) {
list = new LinkedList<Integer>(list.subList(index+1 ,
list.size()));
index = 0;
if (list.size()==1) {
sets.add(new LinkedList<>(list));
}
}
} else {
index++;
if (index + 1 == list.size() || list.size()==1) {
sets.add(new LinkedList<>(list));
}
}
}
if (sets.size() == M) {
finalLists.add(new Seq(new LinkedList<LinkedList<Integer>>(sets)));
}
}
return finalLists;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
GoogleSequence seq = new GoogleSequence();
System.out.println("Type four numbers, N M K L");
LinkedList<Seq> lists = seq.calculate(sc.nextInt(), sc.nextInt(),
sc.nextInt(), sc.nextInt());
System.out.println("POSSIBLE:" + lists.size());
for (Seq linkedList : lists) {
System.out.println(linkedList.toString());
}
}
}
public class SequenceResult
{
public enum SortType
{
ASC,
DESC
}
public List<int> Sequence = new List<int>();
public SortType Sort { get; set; }
}
public List<SequenceResult> SegmentArray(int[] input)
{
List<SequenceResult> result = new List<SequenceResult>();
if (input == null || input.Length <2)
{
throw new ArgumentException();
}
SequenceResult segment = new SequenceResult();
segment.Sequence.Add(input[0]);
segment.Sequence.Add(input[1]);
result.Add(segment);
if (input[1] > input[0])
{
segment.Sort = SequenceResult.SortType.ASC;
}
else
{
segment.Sort = SequenceResult.SortType.DESC;
}
for (int i = 2; i < input.Length; i++)
{
SequenceResult.SortType sort = input[i] > input[i - 1] ? SequenceResult.SortType.ASC : SequenceResult.SortType.DESC;
if (sort == segment.Sort)
{
segment.Sequence.Add(input[i]);
}
else
{
segment = new SequenceResult();
segment.Sort = sort;
segment.Sequence.Add(input[i - 1]);
segment.Sequence.Add(input[i]);
result.Add(segment);
}
}
return result;
}
Test:
[TestMethod()]
public void SegmentArrayTest()
{
Google1 target = new Google1();
int[] input = { 1, 2, 3, 4, 7, 6, 5, 2, 3, 4, 1, 2 };
List<Google1.SequenceResult> actual;
actual = target.SegmentArray(input);
actual.ForEach(m => {
Console.Write(m.Sort + "{");
m.Sequence.ForEach(n =>
{
Console.Write(n + " ");
});
Console.Write("}\r\n");
});
}
Result:
ASC{1 2 3 4 7 }
DESC{7 6 5 2 }
ASC{2 3 4 }
DESC{4 1 }
ASC{1 2 }
This is a tested code in C++:
static int seqRun (int N, int M, int K, int L)
{
int result = 0;
for (int i = 1; i <= K; i++)
{
vector<int> seq;
seq.push_back(i);
result += seqRun(N,M,K,L,0,-1,seq);
}
return result;
}
static int seqRun (int N, int M, int K, int L, int run, int direct, vector<int> seq)
{
int seq_len = seq.size();
if (run > M)
return 0;
if (seq_len == N)
{
if (run < M)
return 0;
for (int i = 0; i < N; i++)
cout << seq[i] << ' ';
cout << endl;
return 1;
}
int result = 0;
int last_val = seq.back();
for (int i = last_val-L; i < last_val; i++)
{
if (i > 0)
{
vector<int> seq2 = seq;
seq2.push_back(i);
if (direct == 0)
{
result += seqRun(N,M,K,L,run,0,seq2);
}
else
{
result += seqRun(N,M,K,L,run+1,0,seq2);
}
}
}
for (int i = last_val+1; i <= last_val+L; i++)
{
if (i <= K)
{
vector<int> seq2 = seq;
seq2.push_back(i);
if (direct == 1)
{
result += seqRun(N,M,K,L,run,1,seq2);
}
else
{
result += seqRun(N,M,K,L,run+1,1,seq2);
}
}
}
vector<int> seq2 = seq;
seq2.push_back(last_val);
result += seqRun(N,M,K,L,run,-1,seq2);
return result;
}
1) Do some pre-processing. For any sequence with N elements, split it into multiple continuous sub sequences:
1.1) Iterate all the elements in the seq, if it is > K, split the seq into two sub sequences that is separated by this element. For example, if K = 4, {1,2,3,4,7,2,3,4,1,2} will become {1,2,3,4}, {2,3,4,1,2} since 7 > 4 and it cannot be a continuous part of the sequence according to the problem description.
1.2) Iterate all sequences in step 1.1, and calculate the difference between adjacent elements, if the difference is > L, split the sequence into two sub sequences. For example, if L=2, {2,3,4,1,2} will become {2,3,4}, {1,2} since 4-1=3>2
After pre-processing, the remaining sequences will satisfy the requirement for K and L. The problem is turned into calculating the total number of runs for all these sequences. If the total number of runs is equal to M, the raw sequence is okay and should be counted.
2) To calculate every run of a subsequence after pre-processing, it is quite simple. I will use an example to illustrate it. Only increasing run will be shown here, and decreasing run can be figured out with the same method. Just compare the number with its previous one, and keep tracking from the first number if it is increasing, if in position i, there is one element is decreasing, start a new run from i+1. For sequence {1 2 3 2 6 4}, you will get several runs {1,2,3}, {2,6}, {4}.
I am not completely sure if a run like {1,2,3} should be counted multiple times because it contains {1}, {2}, {3}, {1,2}, {2,3}, {1,2,3} essentially. If it is needed, it can be calculated easily with some combination formula.
3) Sum all the number of runs to see if it is equal to M.
Use the wiki/Longest_increasing_subsequence algorithm
Change the condition of increasing to increasing or decreasing..
the question is not to find the longest sub-sequence. The question is to calculate ALL possible sequences of N elements having the described property.
public class Pattern {
- Nish March 15, 2012public static void main(String[] args){
int[] input={1,2,3,4,7,6,5,2,3,4,1,2}; //N
int startIndex=0;
int runs=10; //M
int no = 10; //K
int diff = 10; //L
while(startIndex+1<input.length){
if(input[startIndex]<no || input[startIndex+1]<no || Math.abs(input[startIndex]-input[startIndex+1])>diff )
continue;
Mode mode=getMode(input[startIndex],input[startIndex+1]);
System.out.print("{"+input[startIndex]+",");
++startIndex;
while(startIndex+1<input.length){
if(input[startIndex]>no || input[startIndex+1]>no || Math.abs(input[startIndex]-input[startIndex+1])>diff )
break;
if(mode==getMode(input[startIndex],input[startIndex+1])){
System.out.print(input[startIndex]+",");
++startIndex;
}
else
{
break;
}
}
System.out.println(input[startIndex]+"}");
--runs;
if(runs<=0)
return;
}
//System.out.println("Hi");
}
private static Mode getMode(int num1,int num2){
if(num1>num2)
return Mode.Decreasing;
else
return Mode.Increasing;
// return null;
}
}
enum Mode{Increasing,Decreasing}