字符串在算法面试中,单独考察的机会并不多,同样倾向于和一些经典算法(后面会讲的)结合来体现区分度。步子不能跨太大,不然容易扯着x。本节我们照样是先解决只需要数据结构知识做基础就可以解决的字符串问题。
在讲题之前,我首先要给大家点拨两个字符串相关的“基本算法技能”。这两个技能偶尔也会单独命题,但整体来看在综合性题目中的考察频率较高,需要大家着重熟悉、反复练习和记忆,确保真正做题时万无一失。
# 基本算法技能
⚡ 30 秒速记
JS字符串不可变,要「原地」改得先转数组,改完join('')- 先定义「字符」是什么:
length按UTF-16单元算,'😀'.length === 2 Array.from(str)按码点拆,能保住普通emoji;用户眼里的「一个字」要用Intl.Segmenter- 两头往中间比用双指针,统计频次用
Map,连续区间约束用滑动窗口 - 测试要覆盖空串、单字符、重复字符、超长输入,别只测英文
字符串题动手之前,先问清楚题目里的「一个字符」指的是什么。 只处理英文小写字母,按下标取就够了;但 JS 的 length 按 UTF-16 单元计数,'😀'.length 是 2,带 emoji 或组合音标的文本按下标切就会出乱码。Array.from 按码点拆能保住大部分 emoji,按用户看到的字切要用 Intl.Segmenter。口径定了再选工具:比首尾用双指针,数次数用 Map,找连续片段用滑动窗口。
字符串算法的第一步不是立刻写循环,而是定义“字符”和“相等”。只处理英文小写字母时,按索引读取通常足够;一旦输入包含 emoji、组合音标或多语言文本,就要区分 UTF-16 code unit、Unicode code point 与用户看到的字素簇。这个口径会直接决定长度、切分、反转和回文判断是否正确。
确定字符语义后,再根据访问模式选工具:两端向中间收缩用双指针,统计出现次数用 Map,连续区间约束用滑动窗口,复杂模式匹配则考虑状态机或动态规划。每个方案都要补空串、单字符、重复字符和超长输入,不能只验证 ASCII 样例。
💬 面试官追问
-
昵称最多
10个字符,'😀'.length却是2,前后端先统一什么?统一「字符」的口径:
UTF-16单元、Unicode码点,还是用户看到的字。length是单元,Array.from(s).length是码点,按视觉字数要用Intl.Segmenter。三端用同一种,不然前端放行了后端又拦。 -
题目只说输入是英文小写字母,还有必要上
Intl.Segmenter吗?没必要。契约限定了字符集,按下标访问就是对的,写进入参校验和测试就行。一旦需求放开到任意文本,再重新选口径。
-
用
Intl.Segmenter数用户看到的字数,怎么写?[...new Intl.Segmenter('zh', { granularity: 'grapheme' }).segment(s)].length。像'👨👩👧'这种家庭emoji,length是8,Array.from是5,Segmenter才是1。 -
从判断回文扩展成找最长无重复子串,双指针还能复用吗?
对撞指针不行,它只比首尾。这题要用滑动窗口:右边一直扩,遇到重复字符就把左边收到重复位置的后一位,
Map记每个字符最后出现的下标,O(n)。 -
统计一大段文本里每个字符出现次数,用对象还是
Map?Map。对象键会和__proto__、constructor这些原型属性冲突,Map没这问题,遍历还保持插入顺序。