高效构建长度为 n 的开心字符串中第 k 小的字符串

【详细解析】长度为n的开心字符串中字典序第k小的字符串

一、问题描述

1. 核心定义

「开心字符串」需满足两个条件:

  • 仅由小写字母 abc 组成;

  • 字符串中相邻字符不相同(下标从1开始)。

2. 问题要求

给定整数 n(字符串长度)和 k(字典序位次),返回长度为 n 的所有开心字符串按字典序排序后的第 k 个字符串;若开心字符串总数少于 k,返回空字符串。

3. 示例分析

输入 输出 说明
n=1, k=3 "c" 长度为1的开心字符串:["a","b","c"],第3个为"c"
n=1, k=4 "" 仅3个开心字符串,无第4个
n=3, k=9 "cab" 长度为3的开心字符串共12个,按序第9个为"cab"

二、解题思路

1. 先计算开心字符串总数

长度为 n 的开心字符串总数规律:

  • 第1位:有3种选择(a/b/c);

  • 第2位及以后:每位不能与前一位相同,故有2种选择;

  • 总数 = 3 * (2^(n-1))

k > 总数,直接返回空字符串。

2. 字典序构造第k个字符串

核心逻辑:逐位确定字符 ,每一步计算当前选择该字符后剩余位置能生成的开心字符串数量,判断 k 落在哪个分支,逐步缩小范围。

举例(n=3, k=9):

  • 总数 = 322 = 12,9 ≤ 12,有效;

  • 第1位候选:a、b、c,每个字符对应后续2位的组合数=2*2=4;

    • a分支:1-4,9不在此范围,k -= 4 → k=5;

    • b分支:5-8,9不在此范围,k -= 4 → k=1;

    • c分支:9-12,9在此范围,确定第1位为c;

  • 第2位候选:不能为c,即a、b,每个字符对应后续1位的组合数=2;

    • a分支:9-10,k=1在此范围,确定第2位为a;
  • 第3位候选:不能为a,即b、c,组合数=1;

    • b分支:9,确定第3位为b;
  • 最终结果:cab(与示例一致)。

三、完整代码实现

Python 复制代码
class Solution:
    def getHappyString(self, n: int, k: int) -> str:
        # 步骤1:计算长度为n的开心字符串总数
        total = 3 * (2 ** (n - 1))
        if k > total:
            return ""
        
        # 步骤2:逐位构造结果字符串
        res = []
        prev_char = None  # 记录前一位字符,避免相邻重复
        remaining = n     # 剩余需要构造的位数
        current_k = k     # 当前需要找的位次
        
        while remaining > 0:
            # 计算当前位选一个有效字符后,剩余位能生成的组合数
            comb = 2 ** (remaining - 1)
            
            # 按字典序遍历候选字符(a、b、c),排除前一位字符
            for char in ['a', 'b', 'c']:
                if char == prev_char:
                    continue  # 跳过与前一位重复的字符
                # 判断当前k是否落在该字符对应的分支中
                if current_k <= comb:
                    res.append(char)
                    prev_char = char
                    remaining -= 1
                    break
                else:
                    # 不在当前分支,减去该分支的组合数,继续找下一个字符
                    current_k -= comb
        
        return ''.join(res)

四、代码核心解析

1. 总数校验

Python 复制代码
total = 3 * (2 ** (n - 1))
if k > total:
    return ""
  • 先通过公式计算所有可能的开心字符串数量,若k超出范围,直接返回空字符串,避免无效计算。

2. 逐位构造逻辑

Python 复制代码
while remaining > 0:
    comb = 2 ** (remaining - 1)
    for char in ['a', 'b', 'c']:
        if char == prev_char:
            continue
        if current_k <= comb:
            res.append(char)
            prev_char = char
            remaining -= 1
            break
        else:
            current_k -= comb
  • comb:当前字符确定后,剩余 remaining-1 位能生成的开心字符串数量(每位2种选择);

  • 按字典序遍历 a/b/c,排除与前一位重复的字符,保证满足开心字符串定义;

  • current_k 落在当前字符的分支内,确定该字符,更新状态(前一位字符、剩余位数);

  • 若不在,减去该分支的数量,继续检查下一个字符。

五、测试用例验证

测试用例1:n=1, k=3

  • total = 3*1=3,k=3 ≤3;

  • remaining=1,comb=1;

  • 遍历a:3>1 → k=2;

  • 遍历b:2>1 → k=1;

  • 遍历c:1≤1 → 确定c,返回"c"。

测试用例2:n=3, k=9

  • total=3*4=12,k=9 ≤12;

  • 第1位:comb=4;

    • a:9>4 → k=5;

    • b:5>4 → k=1;

    • c:1≤4 → 确定c,remaining=2,prev_char=c;

  • 第2位:comb=2;

    • a(≠c):1≤2 → 确定a,remaining=1,prev_char=a;
  • 第3位:comb=1;

    • b(≠a):1≤1 → 确定b;
  • 最终结果:"cab"(与示例一致)。

测试用例3:n=2, k=7

  • total=3*2=6,k=7>6 → 返回""。

测试用例4:n=10, k=100

  • 执行代码后返回"abacbabacb"(与示例一致)。

六、复杂度分析

时间复杂度

  • 外层循环:执行n次(逐位构造);

  • 内层循环:最多遍历3个字符(a/b/c);

  • 总复杂度:O(n),n≤10,效率极高。

空间复杂度

  • 仅使用有限变量(res列表、prev_char等),空间复杂度O(n)(存储结果字符串)。

七、总结

关键点回顾

  1. 总数预判 :通过公式 3*2^(n-1) 快速判断k是否有效,避免无效计算;

  2. 逐位贪心:按字典序遍历候选字符,通过计算分支组合数确定k所在分支,逐步构造结果;

  3. 去重逻辑:每一步排除与前一位相同的字符,保证满足开心字符串的定义。

该方法无需生成所有开心字符串,而是通过数学计算直接定位第k个字符串,在n≤10的约束下,时间和空间效率均达到最优。

相关推荐
Bert.Cai1 小时前
Python time.sleep函数作用
开发语言·python
shughui1 小时前
Miniconda下载、安装、关联配置 PyCharm(2026最新图文教程)
ide·python·pycharm·miniconda
rgb2gray2 小时前
论文详解 | TWScan:基于收紧窗口的增强扫描统计,实现不规则形状空间热点精准检测
网络·人工智能·python·pandas·交通安全·出租车
小鸡吃米…2 小时前
Python线程同步
开发语言·数据结构·python
清水白石0082 小时前
Python 弱引用深度解析——让缓存不再成为内存泄漏的温床
java·python·缓存
zzb15802 小时前
RAG from Scratch-优化-routing
java·前端·网络·人工智能·后端·python·mybatis
sea12162 小时前
Flask配置MySQL连接信息的最佳实践
python·mysql·flask
XW01059992 小时前
5-6统计工龄
数据结构·python·算法
酱紫学Java2 小时前
数据安全比赛:Python 内置函数实战指南
后端·python·网络安全