今天带来 5 道经典的 Python 面试算法题,出自课程《经典算法解题实战》。
这 5 道题看似简单,但做出来还真有点烧脑。答案也都很有营养,包含了不少 Python 的黑魔法,甚至会让你惊呼:居然还有这种方法,一行代码就能写出来?!
不信,看看第一题~
题目 1 - hashmap 统计字频
Write a method anagram(s,t) to decide if two strings are anagrams or not.
Example
Given s="abcd", t="dcab", return true.
Challenge
O(n) time, O(1) extra space
写一个方法,判断两个字符串是否互为“换位词”,可以理解为:两个字符串包含的不同字符的数量是否相同。
这类问题的常用方法为:遍历两个字符串,统计其中各字符出现的频次,若不等则返回 false
。
有很多简单字符串类面试题都是此题的变形题。
Python 代码
class Solution:
def anagram(self, s, t):
return collections.Counter(s) == collections.Counter(t)
源码分析
两个字符串长度不等时必不可能为变位词 (需要注意题目条件灵活处理)。
初始化含有 256 个字符的计数器数组。
对字符串 s 自增,字符串 t 递减,再次遍历判断
letterCount
数组的值,小于 0 时返回false。
复杂度分析
两次遍历字符串,时间复杂度最坏情况下为 $$O(n)$$,
使用了额外的数组,空间复杂度为 $$O(1)$$。
(课程中还提供了C、Java、C++等多种解法,篇幅有限,欢迎登陆实验楼学习—— https://www.shiyanlou.com/courses/492 )
题目二 - Compare Strings
Compare two strings A and B, determine whether A contains all of the characters in B.
The characters in string A and B are all Upper Case letters.
Example
For A = "ABCD", B = "ABC", return true.
For A = "ABCD" B = "AABC", return false.
有两个字符串 A 和 B,判断 A 是否包含 B 的所有字符。
题目意思是问 B 中的所有字符是否都在 A 中,而不是单个字符。比如 B="AABC" 包含两个「A」,而 A="ABCD" 只包含一个「A」,故返回 false 。做题时注意题意,必要时可向面试官确认。
既然不是类似 strstr 那样的匹配,直接使用两重循环就不太合适了。题目中另外给的条件则是 A 和 B 都是全大写单词,理解题意后容易想到的方案就是先遍历 A 和 B 统计各字符出现的频次,然后比较频次大小即可。嗯,祭出万能的哈希表。
Python
Python 的 dict
就是 hash, 所以 python 在处理需要用到 hash 的地方非常方便。
import collections
class Solution:
def compare_strings(self, A, B):
# return a dict with default value set to 0
letters = collections.defaultdict(int)
for a in A:
letters[a] += 1
for b in B:
if b not in letters:
return False
elif letters[b] <= 0:
return False
else:
letters[b] -= 1
return True
源码解析
异常处理,B 的长度大于 A 时必定返回
false
, 包含了空串的特殊情况。使用额外的辅助空间,统计各字符的频次。
复杂度分析
遍历一次 A 字符串,遍历一次 B 字符串,时间复杂度最坏 $$O(2n)$$
, 空间复杂度为 $$O(26)$$
.
题目 3 - Anagrams
Given an array of strings, return all groups of strings that are anagrams.
Example
Given ["lint", "intl", "inlt", "code"], return ["lint", "inlt", "intl"].
Given ["ab", "ba", "cd", "dc", "e"], return ["ab", "ba", "cd", "dc"].
Note
All inputs will be in lower-case
给定一个字符串数组,返回所有「颠倒字母而成的字」的字符串组。
容易想到的方法为使用双重 for 循环,两两判断字符串数组是否互为变位字符串。但显然此法的时间复杂度较高。还需要 $$O(n)$$ 的数组来记录字符串是否被加入到最终结果中。
Python
class Solution:
# @param strs: A list of strings
# @return: A list of strings
# @return: A list of strings
def anagrams(self, strs):
if len(strs) < 2 :
return strs
result=[]
visited=[False]*len(strs)
for index1,s1 in enumerate(strs):
hasAnagrams = False
for index2,s2 in enumerate(strs):
if index2 > index1 and not visited[index2] and self.isAnagrams(s1,s2):
result.append(s2)
visited[index2]=True
hasAnagrams = True
if not visited[index1] and hasAnagrams:
result.append(s1)
return result
def isAnagrams(self, str1, str2):
if sorted (str1) == sorted(str2):
return True
return False
源码分析
strs 长度小于等于 1 时直接返回。
使用与 strs 等长的布尔数组表示其中的字符串是否被添加到最终的返回结果中。
双重循环遍历字符串数组,注意去重即可。
私有方法
isAnagrams
用于判断两个字符串是否互为变位词。
复杂度分析
私有方法 isAnagrams
最坏的时间复杂度为 $$O(2L)$$
, 其中 $$L$$
为字符串长度。双重 for
循环时间复杂度近似为 $$\frac {1}{2} O(n^2)$$
, $$n$$
为给定字符串数组数目。总的时间复杂度近似为 $$O(n^2 L)$$
. 使用了 Vector String "visited",空间复杂度可认为是 $$O(n)$$
.
题目 4 - Valid Palindrome
Given a string, determine if it is a palindrome,
considering only alphanumeric characters and ignoring cases.
Example
"A man, a plan, a canal: Panama" is a palindrome.
"race a car" is not a palindrome.
Note
Have you consider that the string might be empty?
This is a good question to ask during an interview.
For the purpose of this problem,
we define empty string as valid palindrome.
Challenge
O(n) time without extra memory.
判断字符串是否是回文,只需考虑字母数字字符,忽略大小写。
字符串的回文判断问题,由于字符串可随机访问,故逐个比较首尾字符是否相等最为便利,即常见的『两根指针』技法。此题忽略大小写,并只考虑字母和数字字符。
Python
class Solution:
# @param {string} s A string
# @return {boolean} Whether the string is a valid palindrome
def isPalindrome(self, s):
if not s:
return True
l, r = 0, len(s) - 1
while l < r:
# find left alphanumeric character
if not s[l].isalnum():
l += 1
continue
# find right alphanumeric character
if not s[r].isalnum():
r -= 1
continue
# case insensitive compare
if s[l].lower() == s[r].lower():
l += 1
r -= 1
else:
return False
#
return True
源码分析
两步走:
找到最左边和最右边的第一个合法字符 (字母或者字符)
一致转换为小写进行比较
字符的判断尽量使用语言提供的 API。
复杂度分析
两根指针遍历一次,时间复杂度 $$O(n)$$
, 空间复杂度 $$O(1)$$
.
题目 5 - Longest Palindromic Substring
Given a string S, find the longest palindromic substring in S.
You may assume that the maximum length of S is 1000,
and there exists one unique longest palindromic substring.
Example
Given the string = "abcdzdcab", return "cdzdc".
Challenge
O(n2) time is acceptable. Can you do it in O(n) time.
给定一个字符串 S,找出 S 中最长的回文字符串,你可以假设 S 的最大长度是 1000。
最简单的方案,穷举所有可能的子串,判断子串是否为回文,使用一变量记录最大回文长度,若新的回文超过之前的最大回文长度则更新标记变量并记录当前回文的起止索引,最后返回最长回文子串。
Python
class Solution:
# @param {string} s input string
# @return {string} the longest palindromic substring
def longestPalindrome(self, s):
if not s:
return ""
n = len(s)
longest, left, right = 0, 0, 0
for i in xrange(0, n):
for j in xrange(i + 1, n + 1):
substr = s[i:j]
if self.isPalindrome(substr) and len(substr) > longest:
longest = len(substr)
left, right = i, j
# construct longest substr
result = s[left:right]
return result
def isPalindrome(self, s):
if not s:
return False
# reverse compare
return s == s[::-1]
源码分析
使用 left
, right
作为子串的起止索引,用于最后构造返回结果,避免中间构造字符串以减少开销。
复杂度分析
穷举所有的子串,$$O(C_n^2) = O(n^2)$$
, 每次判断字符串是否为回文,复杂度为 $$O(n)$$
, 故总的时间复杂度为 $$O(n^3)$$
. 故大数据集下可能 TLE. 使用了 substr
作为临时子串,空间复杂度为 $$O(n)$$
.
篇幅有限,今天就介绍到这里,更多算法题目,欢迎来实验楼学习——《经典算法解题实战(免费)》~
地址:https://www.shiyanlou.com/courses/492
????????????点击阅读原文,学习更多算法~