2022-03-11每日刷题打卡
代码源——div2每日一题
01序列 - 题目 - Daimayuan Online Judge
我们称一个字符串为好字符串,指这个字符串中只包含’0’和’1’。
现在有一个好字符串,求这个字符串中’1’恰好出现k次的子串有多少个。
输入格式
第一行给出一个数字k,表示子串中’1’的个数。
第二行给出好字符串。
输出格式
输出一个整数,表示好字符串中有多少个符合条件的子串
数据范围
0≤k≤1e^6, |s|≤1e^6
样例输入1
1
1010
样例输出1
6
前缀和+哈希表。题目要求我们找的是子串中1的个数为k的情况有多少,我们当然可以枚举字串的长度,然后遍历字串的组成情况,如果满足要求则计数器++,但这样的时间复杂度为O(n^2),在这题1e6的数据下显然是会超时的,所以我们想办法把题目转化一下。既然是子串,那就说明是连续的,且字符串除了0就是1,那么我们可以用前缀和的思路来写,这样问题就变成了,区间和为k的情况有多少。我们计算子串的前缀和数组,并把相同前缀和的数量用哈希表记录下来。每一个前缀和为sum的位置,都能和所有前缀和为sum-k的位置形成好子串,所以我们计数器记录的是前缀和为sum的数量*前缀和为sum-k的数量。最后只要把计数器输出即可。
但有一种情况是特殊的,即k=0的情况,如果k=0,那么我们记录的子串数量就是前缀和为sum的情况*前缀和为sum的情况,这显然是不对的,会出现重复的好子串。所以遇到k=0时我们应该特殊处理,它的子串数为:连续ans个0组成的子串,它能形成的不同好子串为:(ans+1) * ans/2。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<map>
#include<unordered_map>
#include<stack>
#include<queue>
typedef long long ll;
typedef pair<int, int>PII;
const int MOD = 1e9 + 7;
const int N = 100100;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int k, l = 0;
cin >> k;
string str;
cin >> str;
int n = str.size();
map<int, ll>mymap;
mymap[0]++;
ll res = 0;
vector<int>v(n + 1), sum(n + 1);
if (k == 0)
{
ll ans = 0;
str += '1';
n++;
for (int i = 0; i < n; i++)
{
if (str[i] == '0')ans++;
else
{
res += (ans + 1) * ans / 2;
ans = 0;
}
}
}
else
{
for (int i = 1; i <= n; i++)
{
v[i] = str[i - 1] - '0';
sum[i] = v[i] + sum[i - 1];
mymap[sum[i]]++;
}
int ans = 0;
while (mymap[ans + k] != 0)
{
res += mymap[ans] * mymap[ans + k];
ans++;
}
}
cout << res << endl;
return 0;
}
飞书——每日一题
402. 移掉 K 位数字
给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。
示例 1 :
输入:num = “1432219”, k = 3
输出:“1219”
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。
贪心+单调栈。贪心是,尽量去掉大的数字,而且是尽量靠前的数字,最高位的数一小,那整体肯定随着变小。
我们用单调来遍历num,当遍历到的元素大于栈顶元素时就入栈(因为我们还不知道这个算不算大,要和后面的元素比较才知道),当遍历到的元素小于栈顶元素时,说明前面的那个元素大了,我们把栈顶元素出栈,并把k–,然后继续拿那个元素和栈顶元素比,直到栈为空或者栈顶元素小于当前元素或者k为0时再把当前元素入栈。
此时栈整体呈单调增的状态,如果遍历完num后k没为0,说明还要继续删,此时只要删末尾的元素即可而不是要追求前面的元素,因为前面的已经足够小了,如果删了只会往大的方向去。之和再把字符串中栈中取出,此时字符串是逆序状态,正好让我们把前导0去掉(可能出现头部元素是0的情况,直接返回显然不对,而因为此时字符串反过来了,前导0就变成了末尾,直接pop_back即可)。最后把字符串反向后返回。
class Solution {
public:
string removeKdigits(string num, int k) {
int n = num.size();
if (n == k)return "0";
string str;
stack<char>sta;
sta.push(num[0]);
for(int i=1;i<n;i++)
{
while(!sta.empty()&&num[i]<sta.top()&&k)
{
sta.pop();
k--;
}
sta.push(num[i]);
}
while(k)
{
sta.pop();
k--;
}
while(!sta.empty())
{
str+=sta.top();
sta.pop();
}
while(str.size()>0&&str.back()=='0')str.pop_back();
reverse(str.begin(),str.end());
return str.size()==0?"0":str;
}
};