当前位置: 首页 > news >正文

LeetCode 411 - 最短独占单词缩写

在这里插入图片描述
在这里插入图片描述

文章目录

    • 摘要
    • 描述
    • 题解答案
    • 题解代码分析
      • 关键点解析
    • 示例测试及结果
    • 时间复杂度
    • 空间复杂度
    • 总结

摘要

今天我们来聊一道 “看起来高冷”、但其实思想非常酷的题:最短独占单词缩写
题目要求:给定一个目标字符串 target,还有一个字典 dictionary,你需要为 target 生成一个 最短可能的缩写字符串,并且这个缩写不能和字典里任意一个单词的缩写“冲突”。
这里的“冲突”指的是:如果字典中某个单词与 target 长度相同,那么它可能拥有与我们为 target 生成的缩写相同的缩写形式,那就不行。我们需要找一个“独一无二”的缩写。
虽然听起来像是字符串暴力生成,但其实这里有非常有意思的 位掩码 + 回溯 +贪心/剪枝 的技巧。本文用 Swift 实战,带你解构思路、理解为何这么做、代码怎么写。

描述

题目大致如下:

  • 给定一个字符串 target(长度为 m ,m ≤ 21);

  • 给定一个字符串数组 dictionary(大小 n ,n ≤ 1000),但我们只关心与 target 长度 相同 的那些单词,因为长度不同的就不会有缩写冲突;

  • 缩写定义:你可以将字符串中的任意一些字符(不必连续)变成数字,数字代表被省略字符的数目。例如:对于 "word",下面都是合法缩写:

    • "word"(不省略任何)
    • "1ord"(省略首字符)
    • "w1rd""wo1d""wor1"
    • "2rd""w2d""wo2"
    • "1o1d""1or1""w1r1"、等
    • "3d""w3""4"(全部省略)
  • 长度定义:缩写中每个数字或字母都算作长度为 1。比如 "a32bc" 长度是 4(a32bc 四个部分算 4)。

  • 要求:找出一个对 target 的缩写,使其 长度最短,并且它 不会和字典中任何一个长度等于 target 的单词的缩写形式相同。如果有多个最短的结果,返回任意一个就行。

举个例子:

  • target = "apple"dictionary = ["blade"] → 返回 "a4",因为 "5""4e" 会和 "blade" 的缩写冲突。
  • target = "apple"dictionary = ["plain","amber","blade"] → 返回 "1p3"(也有其他合法答案如 "ap3""a3e""2p2""3le""3l1")。

题解答案

好,我们说说思路(先通俗,然后再代码)。

  1. 缩写候选生成:因为 target 长度 m ≤ 21,理论上每个字符有“保留原字符”或“用数目代替省略”的两种状态,所以所有候选缩写数是 2^m 。这虽大,但 m 上限 21 是可控的。我们通过位掩码来表示哪些位置保留、哪些位置省略。
  2. 字典冲突检测:对于字典中每个与 target 等长的单词,我们可以预先生成一个 “mask” 表示它与 target 在哪些位置 字符相同,哪些位置不同。这样,当我们某个候选缩写保留了某些位置时,如果它保留的位置都恰好与字典某单词不同 -> 那缩写就与该单词“冲突”。我们可以通过位运算快速判断。
  3. 从短到长寻找合法缩写:我们按候选的缩写长度从小往大枚举(或优先队列),“早找到一个合法的就返回”。
  4. 高效剪枝:在生成候选的时候,我们可以根据字典中“哪些位置必须被保留”来提前剪枝,降低枚举量。

大致流程:

  • 筛出字典中与 target 长度相同的单词,并为每个单词生成一个整数 mask(m 位二进制)→ 标记 target 与其在每个位置是否一致。
  • 候选枚举:遍历 cand 从 0 到 (1<<m)-1,表示哪些位置“保留字符”(bit=1)。如果 cand 与每个字典的 mask 做 & 运算都不为 0 (即保留的位至少有一个与该字典单词在该位不同)→ 表示不会冲突。
  • 在所有合法 cand 中,我们选择对应缩写字符串 长度最短 的。
  • 构造缩写字符串的方法:遍历 m 位,从右往左或左往右,当 cand 在某位是 1 就保留字符;是 0 就开始计数省略直到下一保留,再把计数转数字加入。

题解代码分析

下面是 Swift 实现。虽然 Swift 没有完全同样高效做位运算+优先队列,但我们用可读性较高、逻辑清楚的写法。你可以在 Xcode Playground 或 LeetCode 的 Swift 环境中运行。

import Foundationclass Solution {func minAbbreviation(_ target: String, _ dictionary: [String]) -> String {let m = target.countlet tArr = Array(target)// 筛出与 target 长度相同的字典单词var dictMasks: [Int] = []for word in dictionary {if word.count != m { continue }let wArr = Array(word)var mask = 0for i in 0..<m {if tArr[i] != wArr[i] {mask |= (1 << i)}}dictMasks.append(mask)}// 如果没有长度相同的单词,那么最短缩写就是直接 “m” (全部省略)if dictMasks.isEmpty {return "\(m)"}// 候选中最低长度缩写和其位掩码var bestMask = 0var bestLen = Int.max// 枚举所有 1<<m 候选let total = 1 << mfor cand in 0..<total {let len = abbrLen(mask: cand, m: m)if len >= bestLen { continue } // 已有更短解可跳过// 检查是否与每个字典单词冲突var conflict = falsefor dm in dictMasks {if (cand & dm) == 0 {conflict = truebreak}}if !conflict {bestLen = lenbestMask = cand}}// 构建最终缩写return buildAbbr(target: tArr, mask: bestMask)}// 计算 mask 表示的缩写长度private func abbrLen(mask: Int, m: Int) -> Int {var count = 0var len = 0for i in 0..<m {if (mask & (1 << i)) != 0 {// 有保留字符if count > 0 {len += 1count = 0}len += 1} else {count += 1}}if count > 0 {len += 1}return len}// 构造缩写字符串private func buildAbbr(target: [Character], mask: Int) -> String {let m = target.countvar result = ""var count = 0for i in 0..<m {if (mask & (1 << i)) != 0 {if count > 0 {result += "\(count)"count = 0}result += String(target[i])} else {count += 1}}if count > 0 {result += "\(count)"}return result}
}

关键点解析

  • 我们把 target 长度为 m,然后用 1<<m 枚举所有“保留字符”组合。每种组合 cand 表示哪几个位置保留。
  • dictMasks 用来表示字典中每个与 target 等长单词在每个位置是否 不同:如果 target 与该单词在某位置不同,则该位为 1。
  • 检查冲突:如果 cand & dm == 0,意味着 cand 保留的位置全部与该字典单词 一致,则缩写冲突(因为保留的字符对两者相同,省略的字符数也相同)→ 所以组合无效。
  • 计算缩写长度的方法 abbrLen:遍历每一位,如果保留则长度加 1,遇连续省略则当成一个数字加长度 +1。
  • 构造缩写 buildAbbr:和长度计算类似,遇保留字符输出字母;遇省略区段输出省略计数。
  • 我们在枚举时做了 剪枝:只考虑比当前最短解还短的候选,省去无需枚举。
  • 如果字典中没有与 target 等长的单词,直接返回 "\(m)"(意思是省略全部字符)即可。
  • 注意:枚举 2^m 是指数级,但 m ≤ 21,因此最多 ~2 million 彩组合,在 Swift 中需谨慎,但一般在题目限制内是可行的。生产环境建议做剪枝或位运算优化。

示例测试及结果

下面我们运行几个测试:

let sol = Solution()print(sol.minAbbreviation("apple", ["blade"]))           // 可能输出 "a4"
print(sol.minAbbreviation("apple", ["plain","amber","blade"]))  // 可能输出 "1p3" 或其他同长度合法解
print(sol.minAbbreviation("meeting", ["marketing","reading"])) // 输出 "m6g"(示例,仅作说明)

假设运行结果:

a4
1p3
m6g

这些输出都是合法的:

  • 第一例中 “apple” 与 “blade” 长度同为 5,缩写如 “5”、“4e” 会与 “blade” 的缩写冲突,所以返回 “a4”。
  • 第二例中有多个合法缩写,我们返回一个最短且合法的 “1p3”。
  • 第三例如果字典中单词都是长度不同或无冲突,则可省略更多。

时间复杂度

  • 枚举候选:最多 2^m。
  • 对每个候选,我们可能遍历 dictMasks(最多 n),所以最坏情况复杂度约 O(2^m × n × m)(m 用于构造缩写长度/字符串)。
  • 题目给定 m ≤ 21,n ≤ 1000,并且还有条件 log₂(n) + m ≤ 20,所以在限制内这个指数枚举是可行的。

空间复杂度

  • 存储 dictMasks:最多 n 个整数 → O(n)。
  • 枚举过程中使用少量额外变量 → 辅助空间 O(1)(忽略输出字符串)。
    总的来说:O(n)

总结

这题是字符串 +位运算 +枚举 +剪枝的混合体,挑战点在于如何 高效地生成缩写检测冲突。虽然枚举看似“暴力”,但结合题目限制后其实可行。
几个思路类似适用场景:

  • 在变量命名或缩写生成系统中,确保生成的缩写不会与已有的冲突。
  • 在文档/代码自动简写系统中,生成“最简缩写”并排除已有冲突项。
  • 在编码器或压缩系统里,生成最短的编码并保证唯一性。

总体来说,这题训练的是“多维度约束下枚举 +剪枝”的思维方式,非常值得刷一遍。

http://www.dtcms.com/a/549206.html

相关文章:

  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段-二阶段(11):文法和单词-第三课
  • 网站建设方案和报价家装设计师工资高吗
  • 虚拟环境配置
  • 高效学习《数据库原理》:从理论到实战的计算机专业指南
  • 龙口网站设计网站文件夹没有权限设置
  • Vue3+CSS 实现3D卡片动画
  • RabbitMQ面试全解析:从核心概念到高可用架构
  • MATLAB数据读取全攻略从TXT到音视频的详细方法解析
  • 用ps做网站切片高级感ppt模板免费
  • 21.动态NAT
  • Java的进化之路:从面向对象到云原生时代的全面突破
  • 本期总结与导读
  • 机器学习日报08
  • 【PostgreSQL】Docker下的postgresSQL的查询
  • 衡阳网站seo优化建立网站需要多少钱一个
  • 广州市白云区建设局 网站南同网站建设
  • 东软专业力考试--Java面向对象编程
  • ZCC5050是一款高性能的高侧 OR-ing FET 控制器替代LM5050
  • 织梦网站怎么做索引地图公司注册信息查询系统
  • 扩展阅读:JSON 简介
  • AR眼镜 + 海关:智慧通关技术方案解析|阿法龙XR云平台
  • 海南省建设信息官方网站做网站专业公司
  • DoIP协议常用数据类型及其使用场景详解
  • java应该如何学习
  • PostIn零基础学习,安装与快速入门
  • Rust 模式匹配的穷尽性检查:编译期保障的完整性与安全性
  • 咸阳住房和城乡建设规划局网站浏览器微信登录入口
  • 【赵渝强老师】TiDB PD集群存储的信息
  • 开源启航,智算未来:超算开源社区重磅亮相CNCC 2025
  • 如何做登陆界面的网站百度关键词点击工具