Level
프로그래머스 Lv3
Day
13일
kakao
2020 카카오 블라인드
 
function solution(key, lock) {
    const keySize = key.length;
    const lockSize = lock.length;
    const boardSize = lockSize * 3;
    let ans = false;

//  배열의 x, y 좌표를 자연수 위치 정보로 변환
    const toNumbers = (grid, offset=0, inverted=false) => {
        const numbers = new Set();
        grid.forEach((row, i) => 
                      row.forEach((c, j) => 
                                   (c ^ inverted) && numbers.add(i * boardSize + j + offset)))
        return numbers;
    };

//  자연수 위치 정보를 set으로 전달 받아 board를 기준으로 회전된 set을 리턴
    const rotateInBoard = (numbers) => {
        const rotated = new Set();
        numbers.forEach(num => {
            const rotatedNumber = boardSize * (boardSize -1) + Math.floor(num / boardSize) - boardSize * (num %boardSize)
            rotated.add(rotatedNumber);
        })

        return rotated;
    }

    const keyX = lockSize - keySize + 1;
    const lockX = lockSize;
    let keyNumbers = toNumbers(key, keyX*(1 + boardSize));
    const lockNumbers = toNumbers(lock, lockX*(1 + boardSize));
    const lockEmptyNumbers = toNumbers(lock, lockSize*(1 + boardSize), true);
    
//  key 블락의 개수가 lock의 빈 부분보다 작을 경우 false 리턴
    if(keyNumbers.size < lockEmptyNumbers.size){
        return false;
    }

//  회전/평행이동 후 확인
		// 회전
    for(let i=0; i<4; i++){
        const offsetXDir = [1, -1, -1, 1]
        const offsetYDir = [-1, -1, 1, 1]
        
        keyNumbers = rotateInBoard(keyNumbers); // 회전된 위치 정보

        const lookupSize = lockSize + keySize-1; // 평행 이동할 거리
        for(let j=0; j<lookupSize; j++){
            for(let k=0; k<lookupSize; k++){
                const offset = offsetXDir[i] * j + offsetYDir[i] * k * boardSize;
                let emptyCount = lockEmptyNumbers.size; // 채워야할(빈 부분) 개수

                for(let keySnippet of keyNumbers){
                    const movedKeySnippet = keySnippet + offset;
                    // 평행이동된 열쇠의 위치
                    if(lockNumbers.has(movedKeySnippet)){ 
										// lock 블락과 겹칠 경우 break
                       break;
                    }
                    
                    if(lockEmptyNumbers.has(movedKeySnippet)){
										// lock의 빈 부분을 채울 경우 emptyCount 감소
                       emptyCount--;
                    }
                }

                if(emptyCount===0){ // 모두 채워질 경우 true 반환
                    return true;
                }
            }
        }
    }
    return ans;
}