题意:给出串s,长度<=1e6 求出正好包含k个1的子串个数?(字串位置不同就算不同)
尺取:以l为左端点成立的子串,若第一次在r成立 最后一次在y成立(r后的第一个1在y+1) 则个数为y-r+1
第一次在r成立,[l,r]成立 [l+1,r-1]肯定不成立 dp记录i后的第一个1 复杂都为O(2*n)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+20;
char s[N];
int dp[N];
int main()
{
int k;
while(cin>>k)
{
scanf("%s",s+1);
int n=strlen(s+1);
ll ans=0;
//O(N)
if(k==0)
{
int i=1;
while(s[i])
{
int r=i;
while(s[r]=='0')
r++;
if(r>i)
{
r--;
ll x=r-i+1;
ll y=(1+x)*x/2;
ans+=y;
i=r+1;
}
else
i++;
}
cout<<ans<<endl;
continue;
}
dp[n+1]=n+1;
for(int i=n;i>=1;i--)
dp[i]=s[i]=='1'?i:dp[i+1];
//尺取:以i为左端点成立的子串
ll l=1,r=1,cnt=0,y;
while(s[l])
{
while(s[r]&&cnt<k)
{
if(s[r]=='1')
{
cnt++;
if(cnt==k)
break;
}
r++;
}
if(cnt<k)
break;//没有k个1
//[l,r]成立 [l,y]?
//dp优化预处理出,点r+1后第一个'1'的出现位置-r
ans+=dp[r+1]-r;
if(s[l]=='1')
{
cnt--;
r++;
}
l++;
}
cout<<ans<<endl;
}
return 0;
}
由题意子串1个数为k,则子串和应该为k,cnt[i]记录和为i的前缀个数,容易得到以i结尾的字串和为k的个数为:cnt[p[i]-k]]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+20;
char s[N];
ll cnt[N],p[N];
//cnt[i]:前缀和为i个数
//p[N]:1~i的前缀和
int main()
{
int k;
while(cin>>k)
{
memset(cnt,0,sizeof(cnt));
ll ans=0;
scanf("%s",s+1);
p[0]=0;
cnt[0]=1;//
//计算以i结尾 字串和为k个数
for(int i=1;s[i];i++)
{
p[i]=p[i-1];
if(s[i]=='1')
p[i]++;
if(p[i]>=k)
ans+=cnt[p[i]-k];
cnt[p[i]]++;
}
cout<<ans<<endl;
}
return 0;
}