LeetCode 387简单字符串
字符串中的第一个唯一字符 图解题解
这道题到底在问什么
给定一个字符串 s,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。s 只包含小写字母。
- 输入
- s = "leetcode"
- 输出
- 0
- 输入
- s = "loveleetcode"
- 输出
- 2
- 输入
- s = "aabb"
- 输出
- -1
最优解:一步一步想明白
- 3重复计数太浪费。频率表可以把“每个字符出现几次”一次性记下来。
- 4不能只看频率表里谁是 1,因为哈希表不表达“谁在字符串里更靠前”。第二遍扫描负责保留顺序。
- 5cnt = {}先不急着找答案。没有完整频率前,你看到一个字符时还不知道它后面会不会再次出现。
- 6cnt[ch] = cnt.get(ch, 0) + 1第一次看到 l,计数为 1。但现在还不能返回 0,因为后面可能还有 l。
- 7cnt[ch] = cnt.get(ch, 0) + 1o 也暂时只出现一次。第一遍的任务只是计数,不做答案判断。
- 8cnt[ch] = cnt.get(ch, 0) + 1v 当前也是 1。它最后会成为答案,但必须等完整计数后才能确认。
- 9for ch in s 扫描结束现在才知道 l、o、e 都重复了;v、t、c、d 只出现一次。接下来要按原顺序找第一个计数为 1 的位置。
- 10if cnt[ch] == 1 # cnt["l"] = 2按原字符串顺序重新扫描。下标 0 的 l 虽然最靠前,但频率是 2,不能作为答案。
- 11if cnt[ch] == 1 # cnt["o"] = 2下标 1 的 o 也重复了,继续往右找。
- 12if cnt[ch] == 1: return iv 的频率是 1,并且它是从左往右遇到的第一个唯一字符,所以返回索引 2。
- 15这题最容易漏的是“第一个”。只知道谁出现一次还不够,还要按原字符串顺序返回最靠前的位置。
⚠️ 容易写错的地方
✗ 错:找到唯一字符后返回字符本身
✓ 对:返回它的下标 i
题目要求返回索引。
✗ 错:只看频率表,随便返回一个 count=1 的字符
✓ 对:第二遍按原字符串顺序找
哈希表不保证哪个唯一字符最靠前。
✗ 错:第一遍看到 l 计数为 1 就返回 0
✓ 对:等完整计数后再判断
后面还有一个 l,会让它不再唯一。
完整代码(Python)
Python
class Solution:
def firstUniqChar(self, s):
cnt = {}
for ch in s:
cnt[ch] = cnt.get(ch, 0) + 1
for i, ch in enumerate(s):
if cnt[ch] == 1:
return i
return -1复杂度
时间复杂度
O(n)
第一遍计数 O(n),第二遍定位 O(n)。
空间复杂度
O(1)
只有 26 个小写字母,计数表大小为常数。
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 字符串中的第一个唯一字符 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这道题为什么用「字符串」,换最直接的暴力解会差在哪?+
字符串抓住了本题的结构特征,把暴力解里重复的工作省掉;暴力解通常要多嵌套一层枚举,数据一大就超时。具体对比见上文「暴力解及其卡点」与「最优解逐步推演」两节。
时间复杂度为什么是 undefined?怎么推出来的?+
按上文复杂度小节的推导,时间复杂度为 undefined。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。