关于数组中一些辅助数据结构

这一类题目通常需要一些辅助标记,通常是用到HashMap()或者是二进制的mask

面试题 17.11. 单词距离

有个内含单词的超大文本文件,给定任意两个不同的单词,找出在这个文件中这两个单词的最短距离(相隔单词数)。如果寻找过程在这个文件中会重复多次,而每次寻找的单词不同,你能对此优化吗?
示例:
输入:words = [“I”,“am”,“a”,“student”,“from”,“a”,“university”,“in”,“a”,“city”], word1 = “a”, word2 = “student”
输出:1

虽然题目中的单词会重复,但其实与当前 w o r d [ i ] = = w o r d 2 word[i] ==word2 word[i]==word2 距离最近的 w o r d 1 word1 word1 只有两种情况,即遍历中最新的一个 w o r d [ l a s t 1 ] = = w o r d 1 , l a s t 1 < i word[last1] == word1, last1< i word[last1]==word1,last1<i 或者是下一个 w o r d [ n e x t 1 ] = = w o r d 1 , n e x t 1 > i word[next1] == word1, next1> i word[next1]==word1,next1>i。但其实后一种情况会在遍历到 w o r d [ i ′ ] = = w o r d 1 , i ′ = n e x t 1 word[i'] == word1, i' = next1 word[i]==word1,i=next1时,通过计算 w o r d [ l a s t 1 ′ ] = = w o r d 2 , l a s t 1 ′ = i word[last1'] == word2, last1' = i word[last1]==word2,last1=i得到。因此,每次其实只需要考虑前一种情况就可以。即当 w o r d [ i ] = = w o r d 1 ( w o r d 2 ) word[i] == word1(word2) word[i]==word1(word2)时,比较 a n s ans ans i − l a s t 2 ( l a s t 1 ) i - last2(last1) ilast2(last1)的大小,并更新 l a s t 1 ( l a s t 2 ) = i last1(last2)=i last1(last2)=i即可。

class Solution:
    def findClosest(self, words: List[str], word1: str, word2: str) -> int:
        last1 = last2 = -1
        ans = 100000
        for i in range(len(words)):
            if words[i] == word1:
                if cur_2 != -1:
                    ans = min(ans, i-last2)
                cur_1 = i
            elif words[i] == word2:
                if cur_1 != -1:
                    ans = min(ans, i-last1)
                cur_2 = i
        return ans

若是需要 k k k次查找,那么如果用上述方法复杂度将是 O ( k n ) O(kn) O(kn)。此类题目往往可以用空间换时间。因为要对比的其实是某两个单词在数组中出现的位置,因此可以用一个Map()来记录每个单词在数组中出现的位置,之后查找只需要使用对应词的下标数组就可以得到结果。

拓展

318. 最大单词长度乘积

给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0 。
示例 1:
输入:words = [“abcw”,“baz”,“foo”,“bar”,“xtfn”,“abcdef”]
输出:16
解释:这两个单词为 “abcw”, “xtfn”。
示例 2:
输入:words = [“a”,“ab”,“abc”,“d”,“cd”,“bcd”,“abcd”]
输出:4
解释:这两个单词为 “ab”, “cd”。
示例 3:
输入:words = [“a”,“aa”,“aaa”,“aaaa”]
输出:0
解释:不存在这样的两个单词。

暴力解法是先计算两个字符串间是否有重复字符,然后再计算乘积的值。而基础的判断是否重复需要将两个字符串间的每个字符进行比较,时间复杂度比较高。判断重复很多时候能用到HashMap(),但在这里,因为字母个数26<32,也就是一个int类型的数据大小,那么就可以用更简单的判断是否重复的方法:位运算。也就是用一个26位的二进制来表示,其中为1的表明该字符串含有某个字母。这样,如果两个字符串没重复的数,那么二者&后的结果应该是0。这一类字母或者有限数字的题,都可以考虑一下用这种方法来判断重复,但有一个前提是,不需要知道 顺序,也就是不能用这种方法来进行类似滑动窗口的操作,因为你不知道滑动窗口左侧的数消失时,mask中应该去掉的是哪一位。

class Solution:
    def maxProduct(self, words: List[str]) -> int:
        def mask(word):
            return sum(1 << (ord(c) - ord('a')) for c in set(word))
            #将对应字符串转为二进制mask
        maxx = 0
        for i in range(len(words)):
            for j in range(i+1, len(words)):
                if not mask(words[j]) & mask(words[i]): #两个字符串没有重复的字母
                    maxx = max(len(words[i]) * len(words[j]), maxx)
        return maxx

当然,这题很明显还能优化,比如每个字符串只需要mask()一次并记录,而不需要每次都转换一次;当两个字符串的mask()值相同,如meet和met时,只需要用长度长的就可以。总之,这种需要判断的属性个数有限时,可以想到用二进制掩码来做。

拓展

2135. 统计追加字母可以获得的单词数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值