题目描述
给一个长度为 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;
}

加油!