LeetCode笔记:Weekly Contest 336

1. 题目一

给出题目一的试题链接如下:

1. 解题思路

这一题思路非常的直接,就是在给定的数组范围内看一下每一个word是不是符合以元音作为首尾字符,count一下符合条件的单词即可。

2. 代码实现

给出python代码实现如下:

class Solution:
    def vowelStrings(self, words: List[str], left: int, right: int) -> int:
        cnt = 0
        for w in words[left:right+1]:
            if w[0] in "aeiou" and w[-1] in "aeiou":
                cnt += 1
        return cnt

提交代码评测得到:耗时63ms,占用内存14.1MB。

2. 题目二

给出题目二的试题链接如下:

1. 解题思路

这一题其实也挺简单的,要使得prefix最大,事实上必然就是一个倒序排列,因此我们从大到小对元素重新排个序,然后求一下prefix的值,然后数一下有多少个大于0即可。

2. 代码实现

给出python代码实现如下:

class Solution:
    def maxScore(self, nums: List[int]) -> int:
        nums = sorted(nums, reverse=True)
        s = accumulate(nums)
        res = [x for x in s if x > 0]
        return len(res)

提交代码评测得到:耗时736ms,占用内存27.9MB。

3. 题目三

给出题目三的试题链接如下:

1. 解题思路

这道题本质上考察的就是一个前序数组或者说累积数组。

我们首先将被一个数字均表示为二进制,要使得若干次操作之后所有数字都能够归零,那么也就意味着在这一段子串当中,任何一个位上为1的数字的个数是偶数。因此,我们可以简单粗暴的用一个32位的tuple来对二进制下每一位上的1的个数进行统计,然后考察其累积数组当中各个位上奇偶性相同的位置数目即可。

但是,事实上对于后者我们可以进行一定的优化:

  • 首先,由于我们只考虑奇偶性,因此我们事实上在每一次操作当中都可以直接将其对2取模,这样我们最后就只需要找相同的tuple的个数即可;
  • 其次,对于这个tuple,事实上我们也不需要真的是用一个tuple,而可以直接使用一个int数,用其二进制当中的每一位代表每一位上1出现次数的奇偶性;
  • 最后,如果我们用int型来表示status,事实上我们对2取模的操作也可以直接用一个异或操作来替换,两者效果是完全一样的。

综上,我们就可以对我们的代码进行优化了。

2. 代码实现

给出python代码实现如下:

class Solution:
    def beautifulSubarrays(self, nums: List[int]) -> int:
        n = len(nums)
        s = [0 for _ in range(n+1)]
        for i, x in enumerate(nums):
            s[i+1] = s[i] ^ x
        cnt = Counter(s)
        return sum(x*(x-1)//2 for x in cnt.values())

提交代码评测得到:耗时1067ms,占用内存33.8MB。

4. 题目四

给出题目四的试题链接如下:

1. 解题思路

这一题说来惭愧,也是第一时间没有想到思路,后来浏览了一下别人的解答,想到了一种构建方法之后,才进一步联想到了segment tree,最终才算是把这题给搞定了……

这题的设计来说,不难想象一定是使用segment tree进行完成,因为是给出了n个task对应的起止时间段,但是问题的麻烦点在于说duration时间和起止时间段并不是完全重合的,也就是说对于每一个任务来说,可以给一个很长的起止时间段,但是真正的运行时间可能非常的短。

我在看这道题的时候,也就是卡在了这个地方,但是后来看了别人的解答之后,发现是可以greedy地给出一种时间分配方法的:

  • 我们只需要首先按照终止时间进行从小到大排序,然后每一个任务都尽量安排在其终止时间的末尾完成。

在这种情况下,我们事实上是可以尽可能地令时间的重复利用都最大化的。

于是,剩下的问题就是在这个方案下如何获取最终使用的时间了,而对于这个问题,我们使用segment tree的话事实上就是一个比较常规的问题了。

关于segment tree的东西,后面我会另外单独整理一下,感觉还是蛮常见的,这里就不多做展开了。

2. 代码实现

给出python代码实现如下:

class SegmentTreeNode:
	def __init__(self, val, lbound, rbound, lchild=None, rchild=None):
		self.val = val
		self.lbound = lbound
		self.rbound = rbound
		self.lchild = lchild
		self.rchild = rchild

class SegmentTree:
	def __init__(self, arr):
		self.length = len(arr)
		self.root = self.build(0, self.length-1, arr)
		self.vals = arr

	def feature_func(self, lval, rval):
		return lval + rval

	def build(self, lbound, rbound, arr):
		if lbound == rbound:
			root = SegmentTreeNode(arr[lbound], lbound, rbound)
		else:
			mid = (lbound+rbound) // 2
			lchild = self.build(lbound, mid, arr)
			rchild = self.build(mid+1, rbound, arr)
			val = self.feature_func(lchild.val, rchild.val)
			root = SegmentTreeNode(val, lbound, rbound, lchild, rchild)
		return root

	def update(self, idx, val):
		self.vals[idx] = val
		self._update(idx, val, self.root)
		return

	def _update(self, idx, val, root):
		if root.lbound == root.rbound:
			assert(root.lbound == idx)
			root.val = val
			return
		mid = (root.lbound + root.rbound) // 2
		if idx <= mid:
			self._update(idx, val, root.lchild)
		else:
			self._update(idx, val, root.rchild)
		root.val = self.feature_func(root.lchild.val, root.rchild.val)
		return

	def query(self, lb, rb):
		return self._query(lb, rb, self.root)	

	def _query(self, lb, rb, root):
		if lb == root.lbound and rb == root.rbound:
			return root.val
		mid = (root.lbound+root.rbound) // 2
		if rb <= mid:
			return self._query(lb, rb, root.lchild)
		elif lb > mid:
			return self._query(lb, rb, root.rchild)
		else:
			lval = 	self._query(lb, mid, root.lchild)
			rval = self._query(mid+1, rb, root.rchild)
			return self.feature_func(lval, rval)

class Solution:
    def findMinimumTime(self, tasks: List[List[int]]) -> int:
        max_t = max(x[1] for x in tasks)
        tasks = sorted(tasks, key=lambda x: x[1])
        status = [0 for _ in range(max_t+1)]
        status = SegmentTree(status)
        res = 0
        for st, ed, du in tasks:
            t = status.query(st, ed)
            if t >= du:
                continue
            delta = du-t
            for t in range(ed, st-1, -1):
                if delta == 0:
                    break
                if status.vals[t] == 0:
                    status.update(t, 1)
                    delta -= 1
                    res += 1
        return res

提交代码评测得到:耗时689ms,占用内存15.3MB。

KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值