携程笔试0907

4

题目

游游有一个只包含'0'和'1'的字符串,他想知道这个字符串有多少个好子串?

一个字符串如果是“好串”,那么该字符串的所有前缀,'0'的数量严格大于'1'的数量。

  • 输入描述

输入一个只包含'0'和'1'的字符串,长度不超过100000。

  • 输出描述

输出一个整数,代表答案。

  • 示例1

输入

100

输出

3

分析

熟练度不太行,想到一点思路没来得及写出来...

首先看数据范围,n <= 1e5,所以暴力遍历每个区间必然会超时。我们使用的算法的时间复杂度需要是 O(nlogn) 或者 O(n)。

由定义可以得到,如果一个子串是“好串”,那么依次从它的尾部删去一个字符,剩余的子串依然是“好串”。所以对于每个位置,如果以该位置为开头的最长“好串”长度为 length,那么以该位置为开头的“好串”就有 length 个。

如果把字符串的 '0' 对应到 1,'1' 对应到 -1,那么 [i, j] 为好串的条件就等价于 i 处为 '0' 且前缀和数组 pre 对所有 i<=k<=j 满足 pre[k] >= pre[i]。寻找以 i (下标从1开始)为开头的最长“好串”,就等价于寻找 i 右侧第一个使得 [i,j] 不是“好串”的最小的 j。这等价于寻找 i 右侧使得 pre[j] < pre[i] 的最小的 j,问题转化为典型的单调栈问题。

代码

s = input()
n = len(s)
pre = [0] * (n + 1)
str2num = {"0": 1, "1": -1}
for i in range(1, n + 1):
    pre[i] = pre[i - 1] + str2num[s[i - 1]]

stack = [(n + 1, int(1e9))] # (idx, pre[idx]) 初始化的时候加入最右侧的哨兵
cnt = 0
i = n
j = n # 单调栈未处理过的前缀和数组的最右侧的位置
while i > 0:
    # 对每个 i 找到 i 右侧第一个小于 pre[i] 的 pre[j] 和 j
    if s[i - 1] == "0":
        while j > i:
            while stack and stack[-1][1] >= pre[j]:
                stack.pop()
            stack.append((j, pre[j]))
            j -= 1
        if stack[0][1] >= pre[i]: # 右侧最小值
            cnt += n + 1 - i
        else:
            while stack[-1][1] >= pre[i]:
                stack.pop()
            cnt += stack[-1][0] - i
    i -= 1
print(cnt)

i 和 j 只会向左移动,前缀和数组的每项最多入栈一次、出栈一次,时间复杂度为 O(n)。

#携程笔试##笔试##秋招##携程#
全部评论
这个可以一次遍历解决,记录0,1结尾就行, 设个dp,dp[0] = 0 if nums[0] == 1 else 1, 然后遍历, dp[i] = dp[i-1] + 1 if nums[i] == 0 else dp[i-1] - 1 最后返回sum(dp)就OK了。 分析:记录每个位置结尾的好串个数
6 回复 分享
发布于 2023-09-08 14:51 广东
佬,有其他题目吗,想看看
点赞 回复 分享
发布于 2023-09-07 23:59 北京
推荐 九龙湖摸鱼大师 在 https://www.nowcoder.com/feed/main/detail/3afe8632d56341c19ee4e13f84adb68a?sourceSSR=search 9楼的评论,基于贪心做的,应该是时间空间都最高效的解法了,测试了一下常数是我的 1/5 到 1/6 左右
点赞 回复 分享
发布于 2023-09-08 00:54 北京
第三题我有个很短的写法,一次for循环出来了
点赞 回复 分享
发布于 2023-09-08 01:02 北京

相关推荐

今年会有offer吗:一眼代码相似度过高
投递华为等公司10个岗位
点赞 评论 收藏
分享
14 24 评论
分享
牛客网
牛客企业服务