6050. 字符串的总引力
开始我想的是类似最长回文子串那种,二维dp。本来我是觉得dp[i][j]表示从i到j的子字符串长度,然后从dp[i+1][j]和dp[i][j-1]中计算得到。debug走了一下,在这中间计算的话会有重复值
828. 统计子串中的唯一字符
滑窗,超时
class Solution:
def uniqueLetterString(self, s: str) -> int:
count=0
#滑动窗口
left=-1
while left<len(s)-1:
left+=1
cur=s[left]
count+=1
visited={}
visited[cur]=1
right=left+1
while right<len(s):
cur = s[right]
if cur in visited:
visited[cur]+=1
else:
visited[cur]=1
cur_count=0
for key in visited:
if visited[key]==1:
cur_count+=1
count += cur_count
right += 1
return count
算贡献+组合数学
class Solution:
def uniqueLetterString(self, s: str) -> int:
res=0
#转化为:每个字母所在在子串,包含1个当前字母的子串个数。
for i in range(len(s)):
cur=s[i]
#往左
j=i-1
while j>=0 and s[j]!=cur:
j=j-1
left_count=i-j
#往右
h=i+1
while h<=len(s)-1 and s[h]!=cur:
h=h+1
right_count=h-i
cur_count=left_count*right_count
res+=cur_count
return res
907. 子数组的最小值之和
算贡献+组合数学:超时
注意,left取大于等于,right就取大于,避免重复。类似的情况还有,求数组里某个数右边的第一个最小数字。
class Solution(object):
def sumSubarrayMins(self, arr):
#找贡献,以arr[i]为最小值,的左边界和右边界
res = 0
for i in range(len(arr)):
if i>=1:
left = i-1
while left >= 0:
#left取大于等于,right就取大于,避免重复
if arr[left] >= arr[i]:
left -= 1
else:
break
left_count = i-left
else:
left_count = 1
if i<len(arr)-1:
right = i+1
while right < len(arr) - 1:
if arr[right] >= arr[i]:
right += 1
else:
break
right_count = right - i
else:
right_count = 1
res += (left_count*right_count)*arr[i]
return res
# arr = [3,1,2,4]
arr = [71,55,82,55]
res = Solution().sumSubarrayMins(arr)
print(res)
单调递增栈
单调递增/递减栈,本质上是在求s[i]的左边界和右边界。上面的解法是暴力求s[i]的左边界和右边界,单调递增栈是用空间换时间,维护一个下标数组,来求s[i]的左边界和右边界。
注意:
#保证元素都会被弹出
arr.append(-1)
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
#找贡献,以arr[i]为最小值,的左边界和右边界
#用单调递增栈来求
stack = []
#保证元素都会被弹出
arr.append(-1)
res = 0
for i in range(len(arr)):
while len(stack) > 0 and arr[i] < arr[stack[-1]]:
cur_minv_val_index = stack.pop()
if len(stack) > 0:
left_index = stack[-1]
else:
left_index = -1
right_index = i
res += arr[cur_minv_val_index]*(cur_minv_val_index - left_index)*(right_index - cur_minv_val_index)
stack.append(i)
return res%(10**9+7)
1498 满足条件的子序列数目
超时
class Solution:
def numSubseq(self, nums: List[int], target: int) -> int:
# Sort the array nums.
# nums[i] +nums[j] ≤ target.
# Count the number of subsequences.
res = 0
nums.sort()
for i in range(len(nums)):
flag = False
flag2 = False
for j in range(i, len(nums)):
if nums[i] + nums[j] <= target:
flag = True
continue
else:
#如果走到数组最后都没有break,说明j是满足条件的,不用j-1
flag2 = True
break
# i必须选,剩下的可选可不选(i+1~j)
# 假如i+1~j为4,那么结果是c(4,1)+c(4,2)+c(4,3)+c(4,4)+1
# 其实每个位置可选可不选,每个位置两种情况,2的(i+1~j)次方
if flag == True and flag2 == True:
res += 2 ** (j - 1 - i)
elif flag == True and flag2 == False:
res += 2 ** (j - i)
return res % (10 ** 9 + 7)
排序+二分+计算贡献
排序+二分找边界,类似363. 矩形区域不超过 K 的最大数值和
leetcode-前缀和/差分数组_林冲风雪山神庙的博客-CSDN博客
class Solution:
def numSubseq(self, nums: List[int], target: int) -> int:
nums.sort()
total_len = len(nums)
res = 0
for i in range(total_len):
left, right = i, total_len - 1
while left <= right:
mid = (left + right) // 2
if nums[i] + nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
if left-1 >= i:
#最后一个满足条件的if nums[i] + nums[mid] <= target:left = mid + 1
#所以最后一个满足条件的下标mid=left-1
res += 2**(left-1-i)
return res % (10**9 + 7)
排序+双指针+计算贡献
因为数组是有序的,那么如果nums[l]+nums[r]大于target,比nums[l]往后的数,加上+nums[r]一定也是大于target的
class Solution {
public:
static constexpr int mod = 1e9+7;
int numSubseq(vector<int>& nums, int target) {
const int n = (int)nums.size();
sort(nums.begin(), nums.end());
int l = 0, r = n-1;
int ans = 0;
while (l<=r) {
if (nums[l] + nums[r] > target) {
--r;
} else {
ans = (ans + (1<<(r-l))) % mod;
++l;
}
}
return ans;
}
};
作者:sui-xin-yuan
链接:https://leetcode-cn.com/problems/number-of-subsequences-that-satisfy-the-given-sum-condition/solution/shu-by-sui-xin-yuan-hamy/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
6077. 巫师的总力量和
单调栈+前缀和的前缀和
这个思路有点像子数组范围和
for i in range(len(nums)):
ans += nums[i] * (i - left_max[i]) * (right_max[i] - i)
ans -= nums[i] * (i - left_min[i]) * (right_min[i] - i)
return ans
超时
class Solution(object):
def totalStrength(self, strength):
"""
:type strength: List[int]
:rtype: int
"""
mod = 10**9 + 7
nums = strength
left_min=[-1 for i in range(len(nums))]
right_min=[-1 for i in range(len(nums))]
#最小值,往右边可以辐射到哪里,单调递递增
stack=[]
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]<nums[stack[-1]]:
stack.pop()
if stack and nums[i]>=nums[stack[-1]]:
right_min[i]=stack[-1]
else:
right_min[i]=len(nums)
stack.append(i)
#最小值,往左边边可以辐射到哪里,单调递增
stack=[]
for i in range(len(nums)):
while stack and nums[i]<=nums[stack[-1]]:
stack.pop()
if stack and nums[i]>nums[stack[-1]]:
left_min[i]=stack[-1]
else:
left_min[i]=-1
stack.append(i)
# print(left_min,right_min)
presum = [0 for _ in range(len(nums))]
presum[0] = nums[0]
for i in range(1,len(nums)):
presum[i] = presum[i-1] + nums[i]
ans=0
for i in range(len(nums)):
cur_sum = 0
left_count = i - left_min[i]
right_count = right_min[i] - i
for v in range(left_min[i],i):
if v != -1:
cur_sum -= presum[v] * right_count
for v in range(i,right_min[i]):
cur_sum += presum[v] * left_count
ans += cur_sum * nums[i]
return ans % mod
前缀和的前缀和优化
class Solution(object):
def totalStrength(self, strength):
"""
:type strength: List[int]
:rtype: int
"""
mod = 10**9 + 7
nums = strength
left_min=[-1 for i in range(len(nums))]
right_min=[-1 for i in range(len(nums))]
#最小值,往右边可以辐射到哪里,单调递递增
stack=[]
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]<nums[stack[-1]]:
stack.pop()
if stack and nums[i]>=nums[stack[-1]]:
right_min[i]=stack[-1]
else:
right_min[i]=len(nums)
stack.append(i)
#最小值,往左边边可以辐射到哪里,单调递增
stack=[]
for i in range(len(nums)):
while stack and nums[i]<=nums[stack[-1]]:
stack.pop()
if stack and nums[i]>nums[stack[-1]]:
left_min[i]=stack[-1]
else:
left_min[i]=-1
stack.append(i)
# print(left_min,right_min)
presum = [0 for _ in range(len(nums))]
presum[0] = nums[0]
for i in range(1,len(nums)):
presum[i] = presum[i-1] + nums[i]
presum2 = [0 for _ in range(len(nums))]
presum2[0] = presum[0]
for i in range(1,len(nums)):
presum2[i] = presum2[i-1] + presum[i]
ans=0
for i in range(len(nums)):
cur_sum = 0
left_count = i - left_min[i]
right_count = right_min[i] - i
if i >= 1:
cur_sum += (presum2[right_min[i]-1]-presum2[i-1]) * left_count
else:
cur_sum += presum2[right_min[i]-1] * left_count
if left_min[i]-1 >= 0 and i-1 >= 0:
cur_sum -= (presum2[i-1] - presum2[left_min[i]-1]) * right_count
elif i-1 >= 0 and left_min[i]-1 < 0:
cur_sum -= presum2[i-1] * right_count
# for v in range(left_min[i],i):
# if v != -1:
# cur_sum -= presum[v] * right_count
# for v in range(i,right_min[i]):
# cur_sum += presum[v] * left_count
ans += cur_sum * nums[i]
return ans % mod
2104. 子数组范围和
单调栈
class Solution:
def subArrayRanges(self, nums: List[int]) -> int:
#nums[i]是多少子数组的最大值,以及多少子数组的最小值。相加
#求nums[i]是多少子数组的最大值,找到nums[i]左边和右边的边界,2个单调栈
#求nums[i]是多少子数组的最小值,找到nums[i]左边和右边的边界,2个单调栈。共4个单调栈
left_max=[-1 for i in range(len(nums))]
right_max=[-1 for i in range(len(nums))]
left_min=[-1 for i in range(len(nums))]
right_min=[-1 for i in range(len(nums))]
#最大值,往右边可以辐射到哪里,单调递减栈
stack=[]
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]>nums[stack[-1]]:
stack.pop()
if stack and nums[i]<=nums[stack[-1]]:
right_max[i]=stack[-1]
else:
right_max[i]=len(nums)
stack.append(i)
#最大值,往左边可以辐射到哪里,单调递减栈
stack=[]
for i in range(len(nums)):
while stack and nums[i]>=nums[stack[-1]]:
stack.pop()
if stack and nums[i]<nums[stack[-1]]:
left_max[i]=stack[-1]
else:
left_max[i]=-1
stack.append(i)
#最小值,往右边可以辐射到哪里,单调递递增
stack=[]
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]<nums[stack[-1]]:
stack.pop()
if stack and nums[i]>=nums[stack[-1]]:
right_min[i]=stack[-1]
else:
right_min[i]=len(nums)
stack.append(i)
#最小值,往左边边可以辐射到哪里,单调递增
stack=[]
for i in range(len(nums)):
while stack and nums[i]<=nums[stack[-1]]:
stack.pop()
if stack and nums[i]>nums[stack[-1]]:
left_min[i]=stack[-1]
else:
left_min[i]=-1
stack.append(i)
print(left_max)
print(right_max)
print(left_min)
print(right_min)
ans=0
for i in range(len(nums)):
ans += nums[i] * (i - left_max[i]) * (right_max[i] - i)
ans -= nums[i] * (i - left_min[i]) * (right_min[i] - i)
return ans