题目描述
给一个长度为 n 的 01 序列 s[1],s[2],....,s[n],现在可以至多进行 1 次如下操作:
选择 1 ≤ x < n,将 s 序列变成 s[x+1],s[x+2],.....s[n],s[1],s[2],....s[x]
输出最长的全为 1 的子区间长度。
输入描述:
一个 01 字符串,表示序列 s。(1<= |s| <= 100000)
输出描述:
输出一个整数表示答案。
这题能偷懒。题目中说的操作是找一个节点,把它后面的字串都挪到串的最前面,我们假设字串为1110011,如果不进行操作,那么最大长度是3,而进行的最优操作是把最后的两个1放到前面,最大长度为5,这时我们发现操作后的最大长度为从头开始的最长1串和从尾开始的最长1串的长度和。而101010110110的最优操作是不变,最长为串中的11,长度是2.所以我们可以分别从头从尾扫出最大1串长度,遇到0就停(注意:两个1串不能重合)这两个1串的长度和记录为ans。再把串整个扫一遍,找出里面最长的1串,长度记录为ans3[0]。比较ans与ans3[0]并输出较大的就是我们的结果。
这显然不是正解,我们可以想,进行的操作实际上是把字串复制一遍到串尾,这时便是一个环,把环断开成链,再去里面找。注意求得的数可能比字串本来的长度要长,所以要与原来串长去min.
思路一:
#include<bits/stdc++.h> using namespace std; int main(){ string s; cin>>s; int a=s.length(),ans=0,ans3[2]={0,0},mid,k=1;//mid防止重合 for(int i=0;i<=a-1;i++){ if(s[i]=='1') ans3[1]++; else { ans3[0]=max(ans3[0],ans3[1]); ans3[1]=0; } } for(int i=0;i<=a-1;i++){ if(s[i]=='1') ans++; else { mid=i; break; } } for(int i=a-1;i>=mid;i--){ if(s[i]=='1') ans++; else break; } cout<<max(ans,ans3[0]); return 0; }
思路二:
#include<bits/stdc++.h> using namespace std; const int MAX=2e5+5; int ans,pre[MAX],len; signed main(){ string s; cin>>s;len=s.length(); s=s+s; for( int i=1;i<=s.length();++i)pre[i]=pre[i-1]+(s[i-1]=='1'); for( int i=1,l=1;i<=s.length();++i){ l=i; while(pre[i]-pre[l-1]==i-l+1)++i; ans=max(ans,i-l); } printf("%d\n",min(ans,len)); return 0; }
加油!