问题:给出一个由 0 1 组成的字符串,问有k个1组成的字串有多少个。
例子:k = 2,字符串:01010。
有 101,0101,1010,01010;
先讲一个小技巧(刚不久前学的,没想到用上了)
计算 [某个子区间和为k]的区间 的个数,转化为计算 [区间和为k的区间] 是多少区间的子区间(有点抽象哈)
来个例子:[8, 1, 2, 1, 8],求子区间和为4的区间个数。
我们可以找到区间和为4的区间为[1, 2, 1],然后让这个区间(左边元素个数+1)*(区间右边元素个数+1)得到答案4。
回到上面的题目:我们呢只要找到每k个1的每个区间,对每个区间向左找 0 直到碰到 1,记下0的个数,同样的向右找,记下0的个数。
k等于0时,要求各个只有0的小区间的个数。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
char s[1000100];
ll ans[1000100];
int main() {
ll n, len, u = 0, idx = 1, x = 1, y = 0, an = 0;
scanf("%I64d", &n);
scanf("%s", s);
len = strlen(s);
for(int i=0; s[i]; i++) {
if(s[i] == '1') {
ans[idx++] = i;//记录1的位置,方便计算。
an += (y+1)*y/2; //加上每两个1之间组成的只有0的区间个数
y = 0;
}
if(s[i] == '0') y ++;//记录每两个1之间 0的个数
}
an += (y+1)*y/2; //把最后几个0组成的区间加上
if(n == 0) {
printf("%I64d", an);
return 0;
}
for(int i=1; i+n<=idx; i++) {
//x为左边找到的0的个数加1,y为右边找到的0的个数加1
if(i == 1) x = ans[i] + 1;
else x = ans[i]-ans[i-1];
if(i + n == idx) y = (len-ans[i+n-1]);
else y = ans[i+n]-ans[i+n-1];
u += x*y;
}
printf("%I64d", u);
return 0;
}