首先我们可以考虑暴力的做法:
先从大到小枚举k的长度,然后每次遍历一遍数组,如果当前数字为0,那么就将[ i , i + k )这段区间都翻转一遍,那么时间复杂度为 O( n*n*k ),大概是n的三次方。
那么可以考虑优化一下:
既然要将[ i , i + k )这段区间都翻转一遍,那么我们可以用cnt记录当前位置需要翻转的次数,endc[]数组来表示翻转的结束位置,从i开始遍历时,假设a[i]=0那么cnt=1,那么记录endc[i+k]=1,
那么当我枚举到第i+k的位置时cnt-=end[i](此时的i是之前的i+k位置)那么之后的cnt=0。相当于计数器。那么这样总的时间复杂度就是O(n*n)。
代码附上:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N =5005;
int a[N];
int endc[N*2];//表示结束位置需要减去的次数
int n;
void solve(){
cin>>n;
string s;cin>>s;
for(int k=n;k>=1;k--){//枚举长度
for(int i=1;i<=n;i++)a[i]=s[i-1]-'0';
memset(endc,0,sizeof(endc));
int cnt=0;//表示当前位置需要翻转的次数
for(int i=1;i<=n;i++){
cnt-=endc[i];//[i,i+k)这段加,那么枚举到i+k这个位置要减去之前加的
a[i]^=(cnt&1);//翻转过后的a[i]
if(a[i]==0){
if(i+k<=n+1){//n+1是因为[i,i+k)枚举到的是i+k-1<=n
a[i]=1;
endc[i+k]++;
cnt++;//翻转次数+1
}
else break;//如果枚举的长度超出范围
}
}
bool flag=true;
for(int i=1;i<=n;i++){
if(a[i]==0)flag=false;
}
if(flag){
cout<<k<<"\n";
return;
}
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--){
solve();
}
return 0;
}