슬라이딩 윈도우를 통해 탐색하였다. 단 범위가 어느정도 있으므로, 시간초과가 나올 수 있기 떄문에, 백트래킹을 넣었다.
슬라이딩 윈도우를 통해 5구간 씩 나누어 살펴볼것이다. 재귀를 만드는데 나의 경우에는 depth
, 구간의 시작점, 구간의 끝점, 현재구간에 들어가야하는 문자를 매개변수로 넣었다.
사용한 백트래킹 요소들은 다음과 같다.
수학적으로 접근하자면, 번째 유사 칸토어 비트열의 총 수는 이라는 것을 알 수 있다.
0번째 :
1번째 :
2번째 :
...
번째 : 개
따라서, 현재의 번째 유사 칸토어 비트열의 인덱스가 번째 유사 칸토어 비트열의 , 의 범위에 도달하는지 확인할 수 있다. 번째 유사 칸토어 비트열의 인덱스를 라고 한다면 해당 식은 다음과 같다.
예를들어, 1번째 유사 칸토어 비트열 “11011”
가운데, 1번째 “1”
의 인덱스는 0이며, 해당 수는 2번째 유사 칸토어 비트열에서는 인덱스 0 에서 4번째 까지, 3번째 유사 칸토어 비트열의 에서는 0 에서 24 번째까지 영향을 미치게 된다.
따라서, 해당 식을 통해 영향을 미치는 인덱스만 depth
를 올려 더 탐색해서 시간을 줄일 수 있었다. 또또한 0 을 “00000”
으로 바꿔야 하는데, 문제는 1의 갯수만 구하면 되는 문제이므로, 이 부분도 거르고 재귀를 돌리는 게 더 빠르다.
번째 까지 도달한 경우, 나타날 수 있는 , 경우의 수는 총 4가지 이다. 현재 슬라이딩 윈도우의 좌표를 , 이라고 한다면
”0”
부분은 재귀에 반영되지 않는다. 번째에 도달하는 모든 문자는 “11011”
로 해당 범위에 반영되는 경우 항상 “1”
은 4개이다. )ex. 2번째 유사 칸토어 비트열에서 12~14 사이의 1의 갯수를 찾는 경우.
let ans = 0;
let N,L,R;
function solution(n, l, r) {
[N,L,R] = [n,l-1,r-1];
recur(1,0,4,"11011");
return ans;
}
function recur(d, currL, currR, val) {
if(d>=N) {
if(L<=currL && currL <= R && L<=currR && currR<=R) ans += val[0] == "1" ? 4 : 0;
else if(currL<L && L<=currR && currR<=R) for(let i=L%5; i<=currR%5; i++) val[i]=="1" && ans++;
else if(L<=currL && currL <= R && currR>R) for(let i=currL%5; i<=R%5; i++) val[i]=="1" && ans++;
else if(currL<=L && R<=currR) for(let i=L%5; i<=R%5; i++) val[i]=="1" && ans++;
return;
}
for(let i=currL; i<=currR; i++) {
let tempL = Math.pow(5,N-d)*i;
let tempR = tempL + Math.pow(5,N-d)-1;
if((tempL <L && tempR <L) || (tempL >R && tempR >R)) continue;
val[i%5]=="1" && recur(d+1, i*5, i*5+4, "11011");
}
}