一、问题描述
题目描述
对称就是最大的美学,现有一道关于对称字符串的美学。已知:
- 第1个字符串:R
- 第2个字符串:BR
- 第3个字符串:RBBR
- 第4个字符串:BRRBRBBR
- 第5个字符串:RBBRBRRBBRRBRBBR
相信你已经发现规律了,没错!就是第 i
个字符串 = 第 i - 1
号字符串取反 + 第 i - 1
号字符串;
- 取反(R->B, B->R);
现在告诉你 n
和 k
,让你求得第 n
个字符串的第 k
个字符是多少。(k的编号从0开始)
输入描述
第一行输入一个 T
,表示有 T
组用例;
接下来输入 T
行,每行输入两个数字,表示 n
,k
- 1 ≤ T ≤ 100;
- 1 ≤ n ≤ 64;
- 0 ≤ k < 2^(n-1);
输出描述
输出 T
行表示答案;
- 输出 “blue” 表示字符是 B;
- 输出 “red” 表示字符是 R。
备注
- 输出字符串区分大小写,请注意输出小写字符串,不带双引号。
用例
用例 1
输入:
5
1 0
2 1
3 2
4 6
5 8
输出:
red
red
blue
blue
blue
说明:
- 第 1 个字符串:R -> 第 0 个字符为 R
- 第 2 个字符串:BR -> 第 1 个字符为 R
- 第 3 个字符串:RBBR -> 第 2 个字符为 B
- 第 4 个字符串:BRRBRBBR -> 第 6 个字符为 B
- 第 5 个字符串:RBBRBRRBBRRBRBBR -> 第 8 个字符为 B
用例 2
输入:
1
64 73709551616
输出:
red
说明:
无
解题思路
- 规律分析:第
i
个字符串的长度是2^(i-1)
。第i
个字符串可以看作是第i-1
个字符串取反后拼接第i-1
个字符串。 - 递归关系:如果
k
小于第i-1
个字符串的长度,那么第n
个字符串的第k
个字符与第n-1
个字符串的第k
个字符相同。如果k
大于等于第i-1
个字符串的长度,那么第n
个字符串的第k
个字符与第n-1
个字符串的第k - 2^(i-2)
个字符取反。 - 递归终止条件:当
n
为 1 时,第 0 个字符为 R。
问题分析与规律探索
从题目描述和示例中,我们可以观察到字符串的生成规律。每个新的字符串都是由前一个字符串取反后拼接前一个字符串本身得到的。这个过程可以递归地进行,直到我们到达第一个字符串 “R”。
规律总结
-
递归定义:
- 第
n
个字符串的长度是2^(n-1)
。 - 第
n
个字符串 = 第n-1
个字符串取反 + 第n-1
个字符串。
- 第
-
递归终止条件:
- 当
n = 1
时,字符串为 “R”。
- 当
-
递归关系:
- 如果
k < 2^(n-2)
,则第n
个字符串的第k
个字符与第n-1
个字符串的第k
个字符相同。 - 如果
k >= 2^(n-2)
,则第n
个字符串的第k
个字符与第n-1
个字符串的第k - 2^(n-2)
个字符取反。
- 如果
递归解法
我们可以利用递归关系,通过递归调用逐步减少 n
和 k
,直到 n
为 1 时,直接返回结果。
好的,我们来详细解释这个规律,不使用代码。
规律解释
-
字符串生成规律:
- 第1个字符串:
R
- 第2个字符串:
BR
- 第3个字符串:
RBBR
- 第4个字符串:
BRRBRBBR
- 第5个字符串:
RBBRBRRBBRRBRBBR
- 第6个字符串:
BRRBBRRBRBBRBRBBRBRRBBRRBRBBR
- 第1个字符串:
-
观察规律:
- 每个字符串的长度是
2^(n-1)
。 - 每个字符串可以分为两部分:
- 前半部分是前一个字符串取反。
- 后半部分是前一个字符串本身。
- 每个字符串的长度是
-
递归关系:
- 如果
k
位于第n
个字符串的前半部分(即k < 2^(n-2)
),那么第n
个字符串的第k
个字符与第n-1
个字符串的第k
个字符取反。 - 如果
k
位于第n
个字符串的后半部分(即k >= 2^(n-2)
),那么第n
个字符串的第k
个字符与第n-1
个字符串的第k - 2^(n-2)
个字符相同。
- 如果
-
递归终止条件:
- 当
n = 1
时,字符串为R
,第 0 个字符为R
。 - 当
n = 2
时,字符串为BR
,第 0 个字符为B
,第 1 个字符为R
。
- 当
详细步骤
-
确定
k
位于前半部分还是后半部分:- 计算
mid = 2^(n-2)
。 - 如果
k < mid
,则k
位于前半部分。 - 如果
k >= mid
,则k
位于后半部分。
- 计算
-
递归处理:
- 如果
k
位于前半部分:- 递归调用
get(n-1, k)
,并将结果取反。
- 递归调用
- 如果
k
位于后半部分:- 递归调用
get(n-1, k - mid)
,结果不变。
- 递归调用
- 如果
-
递归终止:
- 当
n = 1
时,返回R
。 - 当
n = 2
时,根据k
的值返回B
或R
。
- 当
用例解释
用例 1
-
输入:
1 0
- 第1个字符串:
R
- 第0个字符:
R
-> 输出red
- 第1个字符串:
-
输入:
2 1
- 第2个字符串:
BR
- 第1个字符:
R
-> 输出red
- 第2个字符串:
-
输入:
3 2
- 第3个字符串:
RBBR
- 第2个字符:
B
-> 输出blue
- 第3个字符串:
-
输入:
4 6
- 第4个字符串:
BRRBRBBR
- 第6个字符:
B
-> 输出blue
- 第4个字符串:
-
输入:
5 8
- 第5个字符串:
RBBRBRRBBRRBRBBR
- 第8个字符:
B
-> 输出blue
- 第5个字符串:
用例 2
- 输入:
64 73709551616
- 通过递归关系,逐步减少
n
和k
,直到n
为 1 或 2。 - 最终确定第
k
个字符为R
-> 输出red
- 通过递归关系,逐步减少
通过这种方式,我们可以高效地找到第 n
个字符串的第 k
个字符,而不需要生成整个字符串,从而避免了内存溢出的问题。
可以发现,其实get(n,k),如果k <= 2^(n-2) ,则相当于 get(n-1, k) 的颜色取反。
二、JavaScript算法源码
以下是 JavaScript 代码的详细中文注释和逻辑讲解,重点在于如何处理大数(BigInt
)以及递归逻辑的实现:
JavaScript 代码实现
const readline = require("readline");
// 创建控制台输入输出接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// 存储输入行
const lines = [];
let t; // 测试用例的数量
// 监听输入事件
rl.on("line", (line) => {
lines.push(line); // 将输入行存入数组
// 当读取到第一行时,解析测试用例的数量 t
if (lines.length === 1) {
t = BigInt(lines[0]); // 将 t 转换为 BigInt 类型
}
// 当读取到所有测试用例时,开始处理
if (t && lines.length === Number(t) + 1) {
lines.shift(); // 移除第一行(t 的值)
const arr = lines.map((line) => line.split(" ").map(BigInt)); // 将每行输入转换为 BigInt 数组
getResult(arr); // 调用主逻辑函数
lines.length = 0; // 清空输入行数组,准备下一组输入
}
});
// 主逻辑函数
function getResult(arr) {
for (let [n, k] of arr) {
console.log(getNK(n, k)); // 对每个测试用例调用 getNK 函数并输出结果
}
}
// 递归函数,计算第 n 个字符串的第 k 个字符的颜色
function getNK(n, k) {
// 如果 n 为 1,直接返回 "red"
if (n === 1n) {
return "red";
}
// 如果 n 为 2,根据 k 的值返回 "blue" 或 "red"
if (n === 2n) {
if (k === 0n) return "blue";
else return "red";
}
// 计算第 n 个字符串的一半长度 half
let half = 1n << (n - 2n); // 1n << (n - 2n) 等价于 2^(n-2)
// 如果 k 大于等于 half,递归处理第 n-1 个字符串的第 k - half 个字符
if (k >= half) {
return getNK(n - 1n, k - half);
} else {
// 否则,递归处理第 n-1 个字符串的第 k 个字符,并根据结果取反
return getNK(n - 1n, k) === "red" ? "blue" : "red";
}
}
代码讲解
1. 输入处理
- 使用
readline
模块从控制台读取输入。 - 第一行是测试用例的数量
t
,后续每行是一个测试用例,包含两个值n
和k
。 - 将输入值转换为
BigInt
类型,以支持大数运算。
2. 主逻辑:getResult
函数
- 遍历每个测试用例,调用
getNK
函数计算结果并输出。
3. 递归逻辑:getNK
函数
- 基本情况:
- 如果
n === 1n
,直接返回"red"
。 - 如果
n === 2n
,根据k
的值返回"blue"
或"red"
。
- 如果
- 递归情况:
- 计算第
n
个字符串的一半长度half
,公式为1n << (n - 2n)
,即2^(n-2)
。 - 如果
k >= half
,递归处理第n-1
个字符串的第k - half
个字符。 - 否则,递归处理第
n-1
个字符串的第k
个字符,并根据结果取反("red"
变"blue"
,"blue"
变"red"
)。
- 计算第
示例解析
输入
2
3 2
4 5
运行结果
blue
red
- 解析:
- 对于
n = 3
,k = 2
:half = 2^(3-2) = 2
。k >= half
,递归处理n = 2
,k = 2 - 2 = 0
。- 对于
n = 2
,k = 0
,返回"blue"
。
- 对于
n = 4
,k = 5
:half = 2^(4-2) = 4
。k >= half
,递归处理n = 3
,k = 5 - 4 = 1
。- 对于
n = 3
,k = 1
:half = 2^(3-2) = 2
。k < half
,递归处理n = 2
,k = 1
。- 对于
n = 2
,k = 1
,返回"red"
。 - 取反后返回
"blue"
。
- 取反后返回
"red"
。
- 对于
总结
- 该代码通过递归和位运算的方式,高效地计算第
n
个字符串的第k
个字符的颜色。 - 使用
BigInt
类型处理大数,确保计算的准确性。 - 代码逻辑清晰,适用于解决类似递归问题的场景。
如果有其他问题,欢迎随时提问!
三、Java算法源码
以下是 Java 代码的详细中文注释和逻辑讲解,重点在于如何处理大数(long
类型)以及递归逻辑的实现:
Java 代码实现
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in); // 创建 Scanner 对象,用于读取输入
int t = sc.nextInt(); // 读取测试用例的数量 t
long[][] arr = new long[t][2]; // 创建一个二维数组,用于存储每个测试用例的 n 和 k
for (int i = 0; i < t; i++) {
arr[i][0] = sc.nextLong(); // 读取 n
arr[i][1] = sc.nextLong(); // 读取 k
}
getResult(arr); // 调用主逻辑函数
}
// 主逻辑函数,处理每个测试用例
public static void getResult(long[][] arr) {
for (long[] nk : arr) {
System.out.println(getNK(nk[0], nk[1])); // 调用 getNK 函数并输出结果
}
}
// 递归函数,计算第 n 个字符串的第 k 个字符的颜色
public static String getNK(long n, long k) {
// 如果 n 为 1,直接返回 "red"
if (n == 1) {
return "red";
}
// 如果 n 为 2,根据 k 的值返回 "blue" 或 "red"
if (n == 2) {
if (k == 0) return "blue";
else return "red";
}
// 计算第 n 个字符串的一半长度 half
long half = 1L << (n - 2); // 1L << (n - 2) 等价于 2^(n-2)
// 如果 k 大于等于 half,递归处理第 n-1 个字符串的第 k - half 个字符
if (k >= half) {
return getNK(n - 1, k - half);
} else {
// 否则,递归处理第 n-1 个字符串的第 k 个字符,并根据结果取反
return "red".equals(getNK(n - 1, k)) ? "blue" : "red";
}
}
}
代码讲解
1. 输入处理
- 使用
Scanner
从控制台读取输入。 - 第一行是测试用例的数量
t
,后续每行是一个测试用例,包含两个值n
和k
。 - 将输入值存储到二维数组
arr
中,arr[i][0]
存储n
,arr[i][1]
存储k
。
2. 主逻辑:getResult
函数
- 遍历每个测试用例,调用
getNK
函数计算结果并输出。
3. 递归逻辑:getNK
函数
- 基本情况:
- 如果
n == 1
,直接返回"red"
。 - 如果
n == 2
,根据k
的值返回"blue"
或"red"
。
- 如果
- 递归情况:
- 计算第
n
个字符串的一半长度half
,公式为1L << (n - 2)
,即2^(n-2)
。 - 如果
k >= half
,递归处理第n-1
个字符串的第k - half
个字符。 - 否则,递归处理第
n-1
个字符串的第k
个字符,并根据结果取反("red"
变"blue"
,"blue"
变"red"
)。
- 计算第
示例解析
输入
2
3 2
4 5
运行结果
blue
red
- 解析:
- 对于
n = 3
,k = 2
:half = 2^(3-2) = 2
。k >= half
,递归处理n = 2
,k = 2 - 2 = 0
。- 对于
n = 2
,k = 0
,返回"blue"
。
- 对于
n = 4
,k = 5
:half = 2^(4-2) = 4
。k >= half
,递归处理n = 3
,k = 5 - 4 = 1
。- 对于
n = 3
,k = 1
:half = 2^(3-2) = 2
。k < half
,递归处理n = 2
,k = 1
。- 对于
n = 2
,k = 1
,返回"red"
。 - 取反后返回
"blue"
。
- 取反后返回
"red"
。
- 对于
总结
- 该代码通过递归和位运算的方式,高效地计算第
n
个字符串的第k
个字符的颜色。 - 使用
long
类型处理大数,确保计算的准确性。 - 代码逻辑清晰,适用于解决类似递归问题的场景。
如果有其他问题,欢迎随时提问!
四、Python算法源码
以下是 Python 代码的详细中文注释和逻辑讲解,重点在于如何处理大数(Python 支持任意大整数)以及递归逻辑的实现:
Python 代码实现
import math # 导入 math 模块,用于数学运算
# 输入获取
t = int(input()) # 读取测试用例的数量 t
# 读取每个测试用例的 n 和 k,并将其转换为 float 类型(虽然题目中 n 和 k 是整数,但代码中使用了 float)
arr = [list(map(float, input().split())) for i in range(t)]
# 算法入口
def getResult(arr):
for n, k in arr: # 遍历每个测试用例
print(getNK(n, k)) # 调用 getNK 函数并输出结果
# 递归函数,计算第 n 个字符串的第 k 个字符的颜色
def getNK(n, k):
# 如果 n 为 1,直接返回 "red"
if n == 1:
return "red"
# 如果 n 为 2,根据 k 的值返回 "blue" 或 "red"
if n == 2:
if k == 0:
return "blue"
else:
return "red"
# 计算第 n 个字符串的一半长度 half
half = math.pow(2, n - 2) # 使用 math.pow 计算 2^(n-2)
# 如果 k 大于等于 half,递归处理第 n-1 个字符串的第 k - half 个字符
if k >= half:
return getNK(n - 1, k - half)
else:
# 否则,递归处理第 n-1 个字符串的第 k 个字符,并根据结果取反
return "blue" if getNK(n - 1, k) == "red" else "red"
# 调用算法
getResult(arr)
代码讲解
1. 输入处理
- 使用
input()
函数从控制台读取输入。 - 第一行是测试用例的数量
t
,后续每行是一个测试用例,包含两个值n
和k
。 - 将输入值存储到列表
arr
中,arr
的每个元素是一个包含n
和k
的列表。
2. 主逻辑:getResult
函数
- 遍历每个测试用例,调用
getNK
函数计算结果并输出。
3. 递归逻辑:getNK
函数
- 基本情况:
- 如果
n == 1
,直接返回"red"
。 - 如果
n == 2
,根据k
的值返回"blue"
或"red"
。
- 如果
- 递归情况:
- 计算第
n
个字符串的一半长度half
,公式为math.pow(2, n - 2)
,即2^(n-2)
。 - 如果
k >= half
,递归处理第n-1
个字符串的第k - half
个字符。 - 否则,递归处理第
n-1
个字符串的第k
个字符,并根据结果取反("red"
变"blue"
,"blue"
变"red"
)。
- 计算第
示例解析
输入
2
3 2
4 5
运行结果
blue
red
- 解析:
- 对于
n = 3
,k = 2
:half = 2^(3-2) = 2
。k >= half
,递归处理n = 2
,k = 2 - 2 = 0
。- 对于
n = 2
,k = 0
,返回"blue"
。
- 对于
n = 4
,k = 5
:half = 2^(4-2) = 4
。k >= half
,递归处理n = 3
,k = 5 - 4 = 1
。- 对于
n = 3
,k = 1
:half = 2^(3-2) = 2
。k < half
,递归处理n = 2
,k = 1
。- 对于
n = 2
,k = 1
,返回"red"
。 - 取反后返回
"blue"
。
- 取反后返回
"red"
。
- 对于
总结
- 该代码通过递归和数学运算的方式,高效地计算第
n
个字符串的第k
个字符的颜色。 - Python 支持任意大整数运算,因此无需担心数值溢出问题。
- 代码逻辑清晰,适用于解决类似递归问题的场景。
如果有其他问题,欢迎随时提问!
五、C/C++算法源码:
以下是 C++ 代码,并附上详细的中文注释和逻辑讲解:
C++ 代码实现
#include <iostream>
#include <vector>
#include <cmath> // 用于 pow 函数
using namespace std;
// 递归函数,计算第 n 个字符串的第 k 个字符的颜色
string getNK(long long n, long long k) {
// 如果 n 为 1,直接返回 "red"
if (n == 1) {
return "red";
}
// 如果 n 为 2,根据 k 的值返回 "blue" 或 "red"
if (n == 2) {
if (k == 0) return "blue";
else return "red";
}
// 计算第 n 个字符串的一半长度 half
long long half = pow(2, n - 2); // 使用 pow 函数计算 2^(n-2)
// 如果 k 大于等于 half,递归处理第 n-1 个字符串的第 k - half 个字符
if (k >= half) {
return getNK(n - 1, k - half);
} else {
// 否则,递归处理第 n-1 个字符串的第 k 个字符,并根据结果取反
return (getNK(n - 1, k) == "red") ? "blue" : "red";
}
}
// 主逻辑函数,处理每个测试用例
void getResult(const vector<vector<long long>>& arr) {
for (const auto& nk : arr) {
cout << getNK(nk[0], nk[1]) << endl; // 调用 getNK 函数并输出结果
}
}
int main() {
// 输入获取
int t;
cin >> t; // 读取测试用例的数量 t
vector<vector<long long>> arr(t, vector<long long>(2)); // 创建一个二维数组,用于存储每个测试用例的 n 和 k
for (int i = 0; i < t; i++) {
cin >> arr[i][0] >> arr[i][1]; // 读取 n 和 k
}
// 调用算法
getResult(arr);
return 0;
}
代码讲解
1. 输入处理
- 使用
cin
从标准输入读取数据。 - 第一行是测试用例的数量
t
,后续每行是一个测试用例,包含两个值n
和k
。 - 将输入值存储到二维向量
arr
中,arr[i][0]
存储n
,arr[i][1]
存储k
。
2. 主逻辑:getResult
函数
- 遍历每个测试用例,调用
getNK
函数计算结果并输出。
3. 递归逻辑:getNK
函数
- 基本情况:
- 如果
n == 1
,直接返回"red"
。 - 如果
n == 2
,根据k
的值返回"blue"
或"red"
。
- 如果
- 递归情况:
- 计算第
n
个字符串的一半长度half
,公式为pow(2, n - 2)
,即2^(n-2)
。 - 如果
k >= half
,递归处理第n-1
个字符串的第k - half
个字符。 - 否则,递归处理第
n-1
个字符串的第k
个字符,并根据结果取反("red"
变"blue"
,"blue"
变"red"
)。
- 计算第
示例解析
输入
2
3 2
4 5
运行结果
blue
red
- 解析:
- 对于
n = 3
,k = 2
:half = 2^(3-2) = 2
。k >= half
,递归处理n = 2
,k = 2 - 2 = 0
。- 对于
n = 2
,k = 0
,返回"blue"
。
- 对于
n = 4
,k = 5
:half = 2^(4-2) = 4
。k >= half
,递归处理n = 3
,k = 5 - 4 = 1
。- 对于
n = 3
,k = 1
:half = 2^(3-2) = 2
。k < half
,递归处理n = 2
,k = 1
。- 对于
n = 2
,k = 1
,返回"red"
。 - 取反后返回
"blue"
。
- 取反后返回
"red"
。
- 对于
总结
- 该代码通过递归和数学运算的方式,高效地计算第
n
个字符串的第k
个字符的颜色。 - 使用
long long
类型处理大数,确保计算的准确性。 - 代码逻辑清晰,适用于解决类似递归问题的场景。
如果有其他问题,欢迎随时提问!