5 道经典 Python 算法题,地铁上也能轻松学会(字符串篇)


今天带来 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)

源码分析

  1. 两个字符串长度不等时必不可能为变位词 (需要注意题目条件灵活处理)。

  2. 初始化含有 256 个字符的计数器数组。

  3. 对字符串 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

源码解析

  1. 异常处理,B 的长度大于 A 时必定返回 false, 包含了空串的特殊情况。

  2. 使用额外的辅助空间,统计各字符的频次。

复杂度分析

遍历一次 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

源码分析

  1. strs 长度小于等于 1 时直接返回。

  2. 使用与 strs 等长的布尔数组表示其中的字符串是否被添加到最终的返回结果中。

  3. 双重循环遍历字符串数组,注意去重即可。

  4. 私有方法 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

源码分析

两步走:

  1. 找到最左边和最右边的第一个合法字符 (字母或者字符)

  2. 一致转换为小写进行比较

字符的判断尽量使用语言提供的 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]

源码分析

使用 leftright 作为子串的起止索引,用于最后构造返回结果,避免中间构造字符串以减少开销。

复杂度分析

穷举所有的子串,$$O(C_n^2) = O(n^2)$$, 每次判断字符串是否为回文,复杂度为 $$O(n)$$, 故总的时间复杂度为 $$O(n^3)$$. 故大数据集下可能 TLE. 使用了 substr 作为临时子串,空间复杂度为 $$O(n)$$.


篇幅有限,今天就介绍到这里,更多算法题目,欢迎来实验楼学习——《经典算法解题实战(免费)》~

地址:https://www.shiyanlou.com/courses/492

????????????点击阅读原文,学习更多算法~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值