题意:
给一个数字 k (k<1e6)和一个长度不超过(1e6) 的字符串 s ,这个字符串只是由0和1组成,问这个字符串能分成几个满足各字符的和等于
k 的子串?
输入:
1 1010
2 01010
100 01010
输出:
6
4
0
分析:直接暴力,分类讨论,情况一:1的数量小于k,输出0。情况二:k=0,这时只需要统计0的个数即可,由此找到规律,能组成
的子串的数目=((0的个数+1)*0的个数 ) / 2。情况三:也是最重要最难的,找到满足这样规则的最短的子串,然后记录该子串的
两个端点的位置,从开头到该字串的末端可以构成的子串数量=左端点 * 右端点,最后全部遍历一遍字符串就可以了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000000+10;
char s[maxn];
int main()
{
int k,i;
long long int sum=0,cur=0,sum2=0;
scanf("%d%s",&k,s);
for(int i=0; i<strlen(s); i++)
{
if(s[i]=='1')
++sum;
}
if(sum<k)
printf("0\n");
if(k==0) ///只统计连续的0,1只做分割
{
long long int sum=0;
long long int cur=0;
for(int i=0; i<strlen(s); i++)
{
if(s[i]=='0')
++cur;
else
{
sum+=(cur)*(cur+1)/2;
cur=0; ///遇到1重置
}
}
sum+=(cur)*(cur+1)/2; ///全是0
printf("%lld\n",sum);
}
long long int left=1,right=0;
for(i=0; i<strlen(s); i++)
{
if(s[i]=='1')
{
left=i+1;
break;
}
}
int pre=0;
cur=0;
for(; i<strlen(s); i++)
{
if(s[i]=='1')
{
++cur;
if(cur>k) ///left从之前right位置开始,临界点
{
right=i-pre;
cur=1;
sum2+=left*right;
left=right;
}
pre=i;
}
}
if(cur==k)
{
right=i-pre;
sum2+=left*right;
}
printf("%lld\n",sum2);
return 0;
}
小结:这个用暴力解决的题目,学的就是一种思路,清晰的思路。