Q792 Number of Matching Subsequences

Given string S and a dictionary of words words, find the number of words[i] that is a subsequence of S.

Example :
Input: 
S = "abcde"
words = ["a", "bb", "acd", "ace"]
Output: 3

Explanation: There are three words in words that are a subsequence of S: 
"a", "acd", "ace".
Note:
All words in words and S will only consists of lowercase letters.
The length of S will be in the range of [1, 50000].
The length of words will be in the range of [1, 5000].
The length of words[i] will be in the range of [1, 50].
解题思路:

思路1:将 S 与 words 中的 word 一个个匹配,计数。时间复杂度很高,超时。见Python实现部分。

思路2:把 S 中各个字符的下标位置用字典列表保存起来(之所以用字典列表,是因为 S 中可能有重复的字母)。见Python实现2部分。

例如,S = "aabcbbac",那么得到的字典列表为:
{ "a" : [0,1,6], "b" : [2,4,5], "c" : [3,7] }

然后对于 words 中的每一个 word,计算 word 中每个字符与字典列表中每个字符的位置关系。这里有很多个小注意点:

举例1:word = "acac", i = 0 时,取 "a" ,0 >= i,因此 "a" 匹配;i =1 时,取 "c",3 >= i,因此 "c" 匹配;但是当 i = 2 时,取 "a",发现字典列表中 "a" 的前两个位置 0、1 都不行,但是第三个位置 6 可以,它满足 6 >= i ,所以也是匹配。因此在判断某个字符匹配时,这里应该是一个for循环,目的是找到能匹配的正确的位置。同理,c 的位置 7 寻找也是一样。

举例2: word = "acca",前 3 个字符都能匹配,这个不用多说。但是当 i = 3 时,取 "a",我们如果采取例子 1 中的方法,会发现 6 >= i,是匹配的。但是我们观察字符串,发现 "acca" 并不是 S 的子序列。因此,举例 1中还缺少限制条件。通过分析可以发现,因为在找上一个字符 "c" 时,S 的位置已经到达了 7 的位置,所以我们在找字典列表时,找的位置要大于 7,因为 6 < 7,所以不可以。因此,我们用一个变量保存上一次找到的 S 的位置,在 if 判断中加入一个限制,即当前在字典列表中寻找的字符下标要大于上一次寻找到的字符下标。 如果遍历完字典列表中的 "a" 的所有位置还是不满足,那么说明不是子序列,返回 0。

Python实现:
# 超时
class Solution:
    def numMatchingSubseq(self, S, words):
        """
        :type S: str
        :type words: List[str]
        :rtype: int
        """
        count = 0
        for word in words:
            count += self.findSubSeq(word, S)
        return count

    def findSubSeq(self, word, S): 
        i = j = 0
        slen, wlen = len(S), len(word)
        while i < slen:
            # 两字符串索引都不能越界 + S 剩余长度要比 word 剩余长度长(减少运行时间) + 当前字符能匹配
            while i < slen and j < wlen and slen - i >= wlen - j and S[i] == word[j]:
                j += 1
                i += 1
            i += 1
        return 1 if j == wlen else 0
Python实现2:
# 不超时
from collections import defaultdict
class Solution2:
    def numMatchingSubseq(self, S, words):
        """
        :type S: str
        :type words: List[str]
        :rtype: int
        """
        def findSubSeq(word): 
            last_i = -1
            for i, v in enumerate(word):
                if not dic_s[v]:  # 如果word中出现S中不存在的字符
                    return 0
                for cur_i in dic_s[v]:
                    if last_i < cur_i and cur_i >= i:  
                        last_i = cur_i 
                        break
                    if cur_i == dic_s[v][-1]:
                        return 0
            return 1

        dic_s = defaultdict(list)
        # 字典列表记录每个字母出现的位置
        for i, v in enumerate(S):
            dic_s[v].append(i)
        count = 0
        for word in words:
            count += findSubSeq(word)
        return count

S = "aabcbbac"
words = ["acac", "acca", "acc"]
print(Solution().numMatchingSubseq(S, words))  # 2
print(Solution2().numMatchingSubseq(S, words))  # 2
补充知识:

1、字典列表的初始化方法:
可以采用 collections 中的 defaultdict(list) 进行初始化。

from collections import defaultdict
dic_s = defaultdict(list)
        # 字典列表记录每个字母出现的位置
        for i, v in enumerate(S):
            dic_s[v].append(i)

2、复制字典的浅拷贝与深拷贝:

  • 如果 A 是一个字典,B = A 是传引用,A、B 会同时改变。
  • 如果 A 是一个字典,B = A.copy() 对于一层来说(比如字典中存储的是字符串)是深拷贝,对于超过一层来说(比如字典中存储的是列表)是浅拷贝。深拷贝修改 A、B 的值相互不影响,但是浅拷贝是相互影响的,即外层不会同时改变,内层(如列表中的元素)会同时改变。所以如果想要复制字符串字典采用 copy() 可行,但复制字典列表不可行。
  • 复制字典不受层数约束的深拷贝方法需要用到 copy 模块中的 deepcopy() 函数。此时,无论字典有几层,A、B都是相互独立的,不会因其中一方的改变而改变。用法如下:
from copy import deepcopy
B = deepcopy(A)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,786评论 0 5
  • 一、快捷键 ctr+b 执行ctr+/ 单行注释ctr+c ...
    o_8319阅读 6,068评论 2 16
  • 1.营养不良的人或者剧烈运动后饮酒,为什么容易出现低血糖? 营养不良的人糖摄入不足,血液血糖水平较低,肝糖原分解加...
    恋学营养阅读 786评论 14 6
  • 再次提起笔,再次画自我介绍,感觉完全是不一样的,中心图想了好久,感觉自己名字不太好画,自己又想突破,所以这张自我介...
    TOP张银阅读 149评论 0 1
  • 我是卓佐,这是我每天一篇原创文章NO.43 初二下班学期,青州中学合并到沙县五中,我终于进入了上中学前梦想着要进的...
    迎庆心烘焙阅读 554评论 0 1

友情链接更多精彩内容