zoukankan      html  css  js  c++  java
  • Leetcode——30.与所有单词相关联的字串【##】


    @author: ZZQ
    @software: PyCharm
    @file: leetcode30_findSubstring.py
    @time: 2018/11/20 19:14

    题目要求:

    给定一个字符串 s 和一些长度相同的单词 words。在 s 中找出可以恰好串联 words 中所有单词的子串的起始位置。
    注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

    示例 1:

    输入:
          s = "barfoothefoobarman",
          words = ["foo","bar"]
    输出: [0,9]
    解释: 从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
    输出的顺序不重要, [9,0] 也是有效答案。
    

    示例 2:

     输入:
            s = "wordgoodstudentgoodword",
            words = ["word","student"]
     输出: []
    这个案例有点迷,题目明明说words里面的单词都是等长的。。。
    

    思路:

    方法一:

    拿到题目,我们需要做的是找出匹配words中所有单词的子串的下标。
    首先,我们用一个hashmap_m1去记录words中出现的单词及其个数,
    然后,开始遍历,依次比较从i,i+words_len的所有子串是否匹配words里单词的某种自由组合,当i的值>s_len-words_num*word_len时,剩余的子串也不够匹配的,i至此停止遍历。
    在内层循环中, 我们设置一个新的hashmap_m2来存储当前子串中匹配到的单词及其个数。
              当该单词不在hashmap_m1中时,匹配失败,break
              当hashmap_m2中该单词的个数超出hashmap_m1中该单词的个数时,匹配失败,break
              当j刚好等于words_nums时,说明匹配成功,则将当前的下标i存入ans中。
    AC代码如下:
    
    class Solution(object):
        def findSubstring(self, s, words):
            """
            :type s: str
            :type words: List[str]
            :rtype: List[int]
            """
            if s == "" or words == []:
                return []
            ans = []
            hash_m1 = {}
            words_num = len(words)
            word_len = len(words[0])
            s_len = len(s)
            if s_len < words_num:
                return []
            for word in words:  # 记录每个单词出现的次数
                if word not in hash_m1:
                    hash_m1[word] = 1
                else:
                    hash_m1[word] += 1
            for i in range(s_len-words_num*word_len+1):
                hash_m2 = {}
                j = 0
                while j < words_num:  # 需要匹配的单词的个数
                    current_word = s[i+j*word_len:i+(j+1)*word_len]
                    if current_word in hash_m1:
                        if current_word not in hash_m2:
                            hash_m2[current_word] = 1
                        else:
                            hash_m2[current_word] += 1
                        if hash_m2[current_word] > hash_m1[current_word]:
                            break
                    else:
                        break
                    j += 1
                if j == words_num:
                    ans.append(i)
            return ans
    

    方法二:

    但是这种方法比较慢,看了网上大神的解法,于是有了方法二。
    这是一个可以达到O(n)的方法。i不是一个字母一个字母的遍历,而是一个单词一个单词的遍历。
    比如说: 
      当前words中所有单词的长度都为3,则遍历的方法是先遍历0,3,6,9,12...,然后扫描1,4,7,10,13,...,再扫描2,5,8,11,14,...,也就是说外层循环i只需要遍历【0,3】即可。
      我们先设置一个hashmap_m1用于保存words中每个单词出现的个数,然后,对于某个i,设置left来记录某个字串的起始下标,设置count来记录匹配成功的单词个数,设置临时变量k遍历【i,len(s)】之后的所有字母,但此时不依次遍历,而是一个单词一个单词的遍历。设置临时字典hashmap_m2来存储在当下循环中匹配到的单词及其个数。
      内层循环中,判断当前的current_word是否在hashmap_m1:
      如果在,则将hashmap_m2中该单词的个数加1,同时判断hashmap_m2中current_word的个数是否超过hashmap_m1中current_word,如果没有超,则将count加1,否则,说明此时匹配到的单词个数不符合words里的单词个数,则从hashmap_m2中去掉最前面的一个单词,然后将count减1,同时将left后移word_len个单位,直至hashmap_m2【current_word】<=hashmap_m1【current_word】。 当count的值与words中单词的个数相同时,说明匹配成功,则将该起始下标left添加到ans中,然后将left后移word_len个单位,同时将count减1。
       如果current_word不在hashmap_m1则,则说明之前的所有匹配都失败,此时应该将hashmap_m2清零,同时将left后移到j+word_len的位置(重新开始匹配)。
    

    AC代码如下:

    class Solution(object):
        def findSubstring(self, s, words):
            """
            :type s: str
            :type words: List[str]
            :rtype: List[int]
            """
            if s == "" or words == []:
                return []
            ans = []
            hash_m1 = {}
            words_num = len(words)
            s_len = len(s)
            if s_len < words_num:
                return []
            for word in words:
                if word not in hash_m1:
                    hash_m1[word] = 1
                else:
                    hash_m1[word] += 1
            word_len = len(words[0]) 
            for i in range(word_len):
                left = i    # 记录起始下标
                count = 0     #记录匹配成功的个数
                hash_m2 = {}   # 记录匹配到的单词及其个数
                j = i   # 从i开始往后遍历
                while j <= s_len - word_len:   # 循环停止条件是遍历到最后一个单词所在位置
                    current_word = copy.deepcopy(s[j:j + word_len])    # 当前单词
                    if current_word in hash_m1:    # 如果单词在hash_m1 中
                        if current_word not in hash_m2:  # 将其加入hash_m2
                            hash_m2[current_word] = 1
                        else:
                            hash_m2[current_word] += 1
    
                        if hash_m2[current_word] <= hash_m1[current_word]:    # 如果该单词在hash_m2中的个数小于等于其在hash_m1中的个数,将count+1
                            count += 1
                        else:  # 否则将hash_m2中最先匹配到的单词剔除,直至hash_m2[current_word] <= hash_m1[current_word]
                            while hash_m2[current_word] > hash_m1[current_word]:
                                temp_word = s[left:left+word_len]
                                hash_m2[temp_word] -= 1
                                if hash_m2[temp_word] < hash_m1[temp_word]:
                                    count -= 1
                                left += word_len
                        if count == words_num:  # 匹配成功
                            if left not in ans:
                                ans.append(left)
                            hash_m2[s[left:left + word_len]] -= 1   # left后移一个单词的长度,hash_m2中最先匹配到的单词移除一个
                            count -= 1
                            left += word_len
                    else:  # 当前单词不在hash_m1中,匹配失败,重新开始匹配,count清零,left设置到当前位置
                        hash_m2.clear()
                        count = 0
                        left = j + word_len
    
                    j += word_len
            return ans
    
  • 相关阅读:
    MySQL之索引优化
    使用Nginx+Lua(OpenResty)开发高性能Web应用
    Eclipse设置背景色
    删除排序数组中的重复项再练习
    计数排序_数组与集合时间比较
    nodejs+redis应用
    redis的一些优缺点
    Redis的线程模型
    GC仅仅是守护线程,空闲执行
    SpringIOC和AOP的生活案例
  • 原文地址:https://www.cnblogs.com/zzq-123456/p/10000437.html
Copyright © 2011-2022 走看看