Фібоначчієві рядки визначаються рекурентно:
Це означає, що рядок F_n складається з двох частин:
перші F_{n-2} символи — це рядок F_{n-2},
наступні F_{n-1} символи — це рядок F_{n-1}.
Отже, щоб знайти -й символ рядка F_n, не потрібно будувати весь рядок. Достатньо визначити, в якій з двох частин він знаходиться.
Позначимо через F_n(k) -ий символ рядка F_n.
Тоді:
якщо k ≤ |F_{n-2}|, то шуканий символ знаходиться в першій частині:
інакше (якщо k > |F_{n-2}|), символ лежить у другій частині:
Таким чином, задача зводиться до пошуку символа в рядках з меншими індексами.
Приклад
Розглянемо рядок F_6, для якого мають місце співвідношення:
F_6 = F_4 + F_5,
|F_4| = 5, |F_5| = 8
Обчислимо F_6(4). Оскільки k = 4 \le |F_4| = 5, то F_6(4) = F_4(4).
Обчислимо F_6(7). Оскільки k = 7 > |F_4| = 5, то F_6(7) = F_5(7 - 5) = F_5(2).
Реалізація алгоритму
У масиві зберігаються довжини фібоначчієвих рядків:
містить довжину рядка F_i.
#define MAX 44
int fib[MAX] = {1, 1};Функція solve повертає -й символ рядка F_n, не будучи сам рядок цілком.
char solve(int n, int k)
{Окремо оброблюємо базові випадки n = 0 і n = 1.
if (n == 0) return 'a';
if (n == 1) return 'b';Відомо, що F_n = F_{n - 2} + F_{n - 1}.
Якщо k \le F_{n - 2}, то -й символ лежить у рядку F_{n - 2}.
Якщо k > F_{n - 2}, то -й символ лежить у рядку F_{n - 1}. Однак його позиція зміщена на довжину першої частини, тому шукаємо символ з номером k - F_{n - 2}.
if (k <= fib[n-2]) return solve(n - 2, k);
return solve(n - 1, k - fib[n-2]);
}Основна частина програми. Обчислюємо перші числа Фібоначчі.
for (int i = 2; i < MAX; i++)
fib[i] = fib[i-1] + fib[i-2];Читаємо кількість тестів .
scanf("%d",&tests);
while (tests--)
{Обчислюємо і виводимо відповідь для кожного тесту.
scanf("%d %d",&n,&k);
printf("%c\n",solve(n,k));
}