Description
给出一个01串,要求将其分成最多的合法段,一个合法段指的是该段不是全部01相间的且长度不超过k
Input
第一行一整数T表示用例组数,每组用例首先输入两个整数n和k表示串长和合法串串长上限,之后一个长度为n的01串
(1<=T<=128,1<=k<=n<=1e5)
Output
输出能够分成最多的段数
Sample Input
Sample Output
1
3
0
2
Solution
dp[i]表示1~i能够分成的最多的段数,那么dp[i]=Max{dp[j]+1},其中j满足i-j<=k且j+1~i不全是01相间的,对于第i个字符,预处理其往左有num[i]的段是01相间的,num序列O(n)扫描一遍即可得到,那么对于i,合法的j就是区间[max(i-k+1,1)-1,i-num[i]-1],所以用线段树维护一个区间最大值即可,单点更新区间查询
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 111111
#define ls (t<<1)
#define rs ((t<<1)|1)
int T,n,k,Min[maxn<<2];
void build(int l,int r,int t)
{
Min[t]=INF;
if(l==r)return ;
int mid=(l+r)/2;
build(l,mid,ls),build(mid+1,r,rs);
}
void push_up(int t)
{
Min[t]=min(Min[ls],Min[rs]);
}
void update(int x,int l,int r,int t,int v)
{
if(l==r)
{
Min[t]=v;
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(x,l,mid,ls,v);
else update(x,mid+1,r,rs,v);
push_up(t);
}
int query(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)return Min[t];
int ans=INF,mid=(l+r)/2;
if(L<=mid)ans=min(ans,query(L,R,l,mid,ls));
if(R>mid)ans=min(ans,query(L,R,mid+1,r,rs));
return ans;
}
char s[maxn];
int num[maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
scanf("%s",s+1);
num[1]=1;
for(int i=2;i<=n;i++)
if(s[i]!=s[i-1])num[i]=num[i-1]+1;
else num[i]=1;
build(0,n,1);
int temp=0;
update(0,0,n,1,0);
for(int i=1;i<=n;i++)
{
temp++;
if(i-num[i]>=max(i-k+1,1))temp=min(temp,query(max(i-k+1,1)-1,i-num[i]-1,0,n,1)+1);
update(i,0,n,1,temp);
if(i==n)printf("%d\n",temp-1);
}
}
return 0;
}