Alright, detective, one of our colleagues successfully observed our target person, Robby the robber. We followed him to a secret warehouse, where we assume to find all the stolen stuff. The door to this warehouse is secured by an electronic combination lock. Unfortunately our spy isn't sure about the PIN he saw, when Robby entered it.
The keypad has the following layout:
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┼───┼───┘
│ 0 │
└───┘
He noted the PIN 1357
, but he also said, it is possible that each of the digits he saw could actually be another adjacent digit (horizontally or vertically, but not diagonally). E.g. instead of the 1
it could also be the 2
or 4
. And instead of the 5
it could also be the 2
, 4
, 6
or 8
.
He also mentioned, he knows this kind of locks. You can enter an unlimited amount of wrong PINs, they never finally lock the system or sound the alarm. That's why we can try out all possible (*) variations.
Can you help us to find all those variations? It would be nice to have a function, that returns an array (or a list in Java/Kotlin and C#) of all variations for an observed PIN with a length of 1 to 8 digits. We could name the function getPINs
(get_pins
in python, GetPINs
in C#). But please note that all PINs, the observed one and also the results, must be strings, because of potentially leading '0's. We already prepared some test cases for you.
Detective, we are counting on you!
풀지 못하여씁니다...
function getPINs(observed) {
return observed.split('')
.map( t => ({
'0': [ '0', '8' ],
'1': [ '1', '2', '4' ],
'2': [ '1', '2', '3', '5' ],
'3': [ '2', '3', '6' ],
'4': [ '1', '4', '5', '7' ],
'5': [ '2', '4', '5', '6', '8' ],
'6': [ '3', '5', '6', '9' ],
'7': [ '4', '7', '8' ],
'8': [ '5', '7', '8', '9', '0' ],
'9': [ '6', '8', '9' ]
}[t]))
.reduce((pre, cur)=> [].concat.apply([], pre.map(t => cur.map(g => t + g))));
}
observed.split('')
.map( t => ({
'0': [ '0', '8' ],
'1': [ '1', '2', '4' ],
'2': [ '1', '2', '3', '5' ],
'3': [ '2', '3', '6' ],
'4': [ '1', '4', '5', '7' ],
'5': [ '2', '4', '5', '6', '8' ],
'6': [ '3', '5', '6', '9' ],
'7': [ '4', '7', '8' ],
'8': [ '5', '7', '8', '9', '0' ],
'9': [ '6', '8', '9' ]
}[t]);
ex) [1,2,3] → [ [ '1', '2', '4' ], [ '1', '2', '3', '5' ], [ '2', '3', '6' ] ]
pre.map(t => cur.map(g => t + g));
1. t = '1'일 때,
cur.map(g => '1' + g);
[ '11', '12', '13', '14', '15' ]을 반환한다.
2. t가 '2'와 '4'일 때에도 동일하게 해보면
전체 map 메서드는 [ ["11", "12", "13", "15"], ["21", "22", "23", "25"], ["41", "42", "43", "45"] ]의 새로운 배열을 만든다.
array.concat
인자로 주어진 배열이나 값을 기존 배열에 합쳐서 새 배열을 반환한다.
하지만, 여기서 핵심은 배열을 합친다면, 배열을 한번 제거해서 합친다. 예제로 알아보자.
[1,2,3].concat(4,5,6); // [1,2,3,4,5,6]
[1,2,3].concat([4,5,6); // [1,2,3,4,5,6]
[1,2,3].concat([[4]]); // [1,2,3,[4]]
[1,2,3].concat([4],5,6,[[7]]); // [1,2,3,4,5,6,7]
function.apply(thisArg, [argsArray])
thisArg
첫번째 인자는 함수를 호출하는데 제공되는 this값이 된다.
argrsArray
함수가 호출되어야 하는 인수
이제 2 메서드를 합쳐보자.
// when pre는 [ '1', '2', '4' ], cur = [ '1', '2', '3', '5' ]
[].concat.apply([], pre.map(t => cur.map(g => t + g)));
// equals to
[].concat.apply([], [["11", "12", "13", "15"], ["21", "22", "23", "25"], ["41", "42", "43", "45"]]);
여기서 apply 메서드의 2번째 인자에 대해서 조금 더 살펴보면, 함수가 호출되어야 하는 인수가 [], 배열로 묶여 있다. 그리하여
[].concat.apply([], [["11", "12", "13", "15"], ["21", "22", "23", "25"], ["41", "42", "43", "45"]]);
// equals to
[].concat(["11", "12", "13", "15"],["21", "22", "23", "25"],["41", "42", "43", "45"]);
여기서 concat 메서드에 의하여서 최종 값은 ["11", "12", "13", "15", "21", "22", "23", "25", "41", "42", "43", "45"]이 된다.
이후 reduce 함수에 의하여서 pre = ["11", "12", "13", "15", "21", "22", "23", "25", "41", "42", "43", "45"], cur = [ '2', '3', '6' ]이 되고, 위 과정을 반복한다.