最近在准备面试,尤其对 Apple 这种大公司挺感兴趣。之前我在温哥华一个小公司待过一阵,那边程序员圈子也挺活跃,经常能听到一些面试经验分享。这次就聊聊我准备 Apple 面试时碰到的一些算法问题,特别是 leetcode 438(Find All Anagrams in a String)这个。
438 这题是从一个字符串 s 里找出所有是 p 的字母异位词的子串起始下标。题本身不难理解,但实现起来还是得有点技巧。我最开始直接暴力,把所有长度等于 len(p) 的子串都遍历一遍,再看是不是 p 的字母异位词。简单粗暴,但效率太低了,字符串一长就不行。
后来我参考了一些网上的解法,发现可以用滑动窗口加计数数组来做。具体就是一个固定大小(len(p))的窗口在 s 上移动,同时维护一个长度 26 的计数数组,记录当前窗口里各个字符的数量。每次窗口右移就把新进来的字符加上、把滑出去的字符减掉,再看当前窗口的计数数组和 p 的是不是完全一致。一致就说明找到了一个异位词子串。
这个方法时间复杂度是 O(n),空间复杂度是 O(1)(因为只有 26 个小写字母),比之前的暴力解法高效得多。下面是我用 Python 写的代码:
def findAnagrams(s, p):
if len(p) > len(s):
return []
p_count = [0] * 26
s_count = [0] * 26
result = []
for ch in p:
p_count[ord(ch) - ord('a')] += 1
for i, ch in enumerate(s):
s_count[ord(ch) - ord('a')] += 1
if i >= len(p):
s_count[ord(s[i - len(p)]) - ord('a')] -= 1
if s_count == p_count:
result.append(i - len(p) + 1)
return result
跑一下验证:findAnagrams("cbaebabacd", "abc") 返回 [0, 6],findAnagrams("abab", "ab") 返回 [0, 1, 2],跟题目给的样例对得上。
这段代码先统计 p 中每个字符的数量,然后用滑动窗口在 s 上移动,不断更新窗口内的字符数量。当窗口内的计数与 p 相同时,就找到了一个异位词子串起点。
这种题考察的还是对数据结构和算法的理解,以及怎么高效解决问题。就先写到这。
