前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Leetcode 【524、767、1053、1079】

Leetcode 【524、767、1053、1079】

作者头像
echobingo
发布2019-07-01 10:06:14
6930
发布2019-07-01 10:06:14
举报
问题描述:【Two Pointers】524. Longest Word in Dictionary through Deleting
解题思路:

这道题是给一个字符串s和一个单词数组,找到数组里面最长的单词,该单词可以通过删除s的某些字符来得到。如果答案不止一个,返回长度最长且字典序最小的单词。如果答案不存在,返回空字符串。

双指针法。对于单词数组中的每个单词 word,字符串 s 和 word 逐字符比较向后滑动。word 如果能滑到最后,说明可以由 s 删除字符得到,这时更新最大长度。如果下一个 word 的最大长度和上一个 word 最大长度一样,则比较它们的字典序,选取较小的字典序(ans = min(ans, word) 即可,ans 为上一个结果)。

时间复杂度为 O(len(d)*len(s)),d 为单词数组长度,s 为字符串的长度;空间复杂度为 O(1)。

Python3 实现:
代码语言:javascript
复制
class Solution:
    def findLongestWord(self, s: str, d: List[str]) -> str:
        ans = ""
        max_len = 0
        for word in d:
            if len(s) < len(word) or len(word) == 0: continue
            j = 0
            for i in range(len(s)):
                if s[i] == word[j]:  # 对应字符相同
                    j += 1
                if j == len(word):
                    if len(word) > max_len:
                        ans = word
                        max_len = len(word)
                    elif len(word) == max_len:   # 长度相同,保留最小字典序的word
                        ans = min(ans, word)
                    break  # 该word判断完成,终止循环
        return ans

问题描述:【Sort、Heap】767. Reorganize String
解题思路:

这道题是给一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

首先可以得知,如果某字符的次数超过 (len(S)+1) // 2,那么一定不可以重构字符串,返回空串。

方法1(Sort):

以 S = "acbaa" 为例,先按照 S 的每个字母出现的次数从大到小排列,得到一个列表,如 A = ['a','a','a','b','c'],然后建立一个和 S 相同长度的列表 ans = [None] * len(S),将 A 中的字符按顺序先安排在 ans 的偶数位置上(ans = ['a',None, 'a', None, 'a']),偶数位置放满后,将剩下一半数字放在奇数位置上。这样即可实现相邻字母不同。

更一般的,如 S = "aaabbbccce",我们将会得到 ans = ['a','b','a','c','a','c','b','c','b','e']。

Python3 实现:

代码语言:javascript
复制
class Solution:
    def reorganizeString(self, S: str) -> str:
        N = len(S)
        A = []
        for (cnt, s) in sorted([(S.count(s), s) for s in set(S)], reverse=True):  # 按字母次数从大到小排列
            if cnt > (N + 1) // 2:  return ""  # 任何一个字符的出现次数不能超过(N+1)//2
            A.extend(s * cnt)
        ans = [None] * N
        ans[::2], ans[1::2] = A[:(N+1)//2], A[(N+1)//2:]  # 将A中前一半字符安排在偶数位置上,后一半安排在奇数位置上
        return "".join(ans)

方法2(Heap):

按照字母出现的次数建立大根堆(实际上可以转化为按照字母出现的次数的负值建立小根堆,即 (-次数, 字母))。然后,每次从堆里面取出两个元素,依次加入到结果 ans 中,并将它们对应的次数减 1。如果不为 0,重新放入堆中。

这其实是一种贪婪的策略,每次取出的两个元素肯定是不相邻的。

例如,S = "abcaa",我们建立的堆的过程为:

代码语言:javascript
复制
       (-3, 'a')                        (-2, 'a')                          (-1, 'a')
        /      \         ->               /                   ->   
(-1, 'b')  (-1, 'c')                  (-1, 'c')     
  • 先取出两个元素,(-3, 'a') 和 (-1, 'b'),加入到结果 ans = "ab",然后次数各减 1,得到 (-2, 'a') 和 (0, 'b')。因为 'a' 不为 0,因此加入到堆中继续判断;
  • 再取出两个元素,(-2, 'a') 和 (-1, 'c'),加入到结果 ans = "abac",然后次数各减 1,得到 (-1, 'a') 和 (0, 'c')。因为 'a' 不为 0,因此加入到堆中继续判断;
  • 堆中元素数量小于 2,终止判断,将最后的一个字符 'a' 加入到结果,ans = "abaca",即得到了正确的答案。

Python3 实现:

代码语言:javascript
复制
import heapq
class Solution:
    def reorganizeString(self, S: str) -> str:
        N = len(S)
        heap = []
        for s in set(S):
            cnt = S.count(s)
            if cnt > (N + 1) // 2:
                return ""
            heapq.heappush(heap, (-cnt, s))  # 建立小根堆(-次数,字母)
        ans = ""
        while len(heap) >= 2:  # 堆中至少有两个元素
            cnt1, s1 = heapq.heappop(heap)  # 每次取出堆顶两个元素
            cnt2, s2 = heapq.heappop(heap)
            ans += s1 + s2  # 依次加入的结果
            cnt1 += 1  # 次数减少(存的是负值)
            cnt2 += 1
            if cnt1 < 0:  # 次数没有减少到0重新加入堆中
                heapq.heappush(heap, (cnt1, s1))
            if cnt2 < 0:
                heapq.heappush(heap, (cnt2, s2))
        if len(heap) == 1:  #如果S长度为奇数
            ans += heapq.heappop(heap)[1]
        return ans

问题描述:【Math】1053. Previous Permutation With One Swap
解题思路:

这道题是给一个正整数数组 A,返回可在一次交换(交换两数字 A[i] 和 A[j] 的位置)后得到的、按字典序排列小于 A 的最大可能排列。

这道题做了好久,前面提交了几次都 WA,就放下了。过了一周后再看这道题,突然有了思路,很快 AC 了。而且弄明白题意后,代码很简单。

很明显,这道题需要找规律。我们先观察以下几组输入输出数据:

[1,1,5] -> [1,1,5] [1,9,4,6,7] -> [1,7,4,6,9] [1,9,4,6,10] -> [1,6,4,9,10] [3,1,1,3] -> [1,3,1,3] [9,3,2,2,3] -> [9,2,3,2,3] [8,5,7,2,4] -> [8,5,4,2,7]

由此,我们观察到规律,需要交换的第一个位置 first 是从右到左遍历的第一个逆序对 A[i] > A[j] 中 i 的位置(如 [8,5,7,2,4] 中从右到左遍历第一个逆序对为 7 > 2,则交换的第一个位置就是 first = 2)。如果不存在逆序对,说明原序列非严格递增(如 [1,1,5]),直接返回原数组即可。第二个交换的位置 second 是从 first 的下一个位置开始,小于 A[first] 且最靠近 A[first] 的最大值的索引位置(如 [1,9,4,6,10] 中,first = 1,小于 A[1] = 9 的最大值为 6,其对应索引 second = 3;再比如 [3,1,1,3] 中,first = 0,小于 A[0] = 3 的最大值是 1,但是要选择最靠近 A[first] 的 1,即 second = 1 而不是 2)。最后,交换 first 和 second 位置的元素即可得到答案。时间复杂度为 O(n)。

Python3 实现:
代码语言:javascript
复制
class Solution:
    def prevPermOpt1(self, A: List[int]) -> List[int]:
        switch = -1  # 第一个数交换的位置
        for i in range(len(A) - 1, 0, -1):
            if A[i] < A[i-1]:
                switch = i - 1
                break
        if switch == -1: # 升序,不交换
            return A
        second_max = ind = 0  # 第二个数及其交换的位置
        for i in range(switch + 1, len(A)):
            if second_max < A[i] < A[switch]:  # 小于A[switch]且离A[switch]的最大值
                second_max, ind = A[i], i
        A[switch], A[ind] = A[ind], A[switch]  # 交换两个位置的元素
        return A

问题描述:【BFS】1079. Letter Tile Possibilities
解题思路:

这道题是给一个字符串,返回所有非空字母序列的数目。

看到数据范围为 <= 7,因此用回溯法去做,对不同长度的字母序列进行全排列,并保存到集合中(去重)。

Python3 实现:
代码语言:javascript
复制
class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        def search(k, path):
            for i in range(N):
                if not b[i]:
                    path += tiles[i]
                    b[i] = True
                    ans.add(path)  # 不同长度的字母序列都保存
                    if k != N:
                        search(k+1, path)
                    b[i] = False
                    path = path[:-1]
                
        ans = set()  # 防止重复
        N = len(tiles)
        b = [False] * len(tiles)
        search(1, "")
        return len(ans)

还可以使用 Python 中 itertools 中自带的 permutations(str/list, k) 函数进行全排列统计,一行代码即可搞定:

代码语言:javascript
复制
class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        return sum(len(set(itertools.permutations(tiles, i))) for i in range(1, len(tiles) + 1))

其中,itertools.permutations(tiles, i) 得到不同长度的全排列,set 去重,len 取返回集合的长度,sum 对不同长度的序列求和。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.06.29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题描述:【Two Pointers】524. Longest Word in Dictionary through Deleting
  • 解题思路:
  • Python3 实现:
  • 问题描述:【Sort、Heap】767. Reorganize String
  • 解题思路:
  • 问题描述:【Math】1053. Previous Permutation With One Swap
  • 解题思路:
  • Python3 实现:
  • 问题描述:【BFS】1079. Letter Tile Possibilities
  • 解题思路:
  • Python3 实现:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档