单调队列+前缀和
本题需要用到上题的破环成链的思想,因为可以正反方向两个方向找切割点,因此我们需要正反两个方向的前缀和,当我们反向搜索时,没必要从size()-1开始,可以同样设下标为1,然后从2*len-1开始加前缀和.
这里有几个需要注意的点:
- 这里需要将字符串转化成数字,如果在首位插入字符凑前缀和会导致TLE
- 数组不宜开过多,否则MLE
- 会有重复切割点
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int N = 1e6+10; 6 int sumc[N*2],q[N*2],a[2*N]; 7 char str[N]; 8 bool can[N]; 9 int main() 10 { 11 freopen("in.txt","r",stdin); 12 int n,kcase = 0; 13 scanf("%d",&n); 14 while(n--){ 15 int hh = 0,tt = -1,cnt = 0; 16 scanf("%s",str+1);// s.insert(0,"A");可能是字符串太长超时,所以我们最好不要对字符串操作 17 int len = strlen(str+1); 18 fill(can+1,can+N+1,0); 19 for(int i=1;i<=len;i++){ 20 a[i]=(str[i]=='C')?1:-1; 21 a[len+i] = a[i]; 22 }//suma>=sumb 23 for(int i=1;i<=2*len;i++) sumc[i] = sumc[i-1]+a[i]; 24 for(int i=1;i<2*len;i++){//a[i]内是两倍的字符串,最后一个不要取 25 while(hh<=tt&&i-len+1>q[hh]) ++hh; 26 while(hh<=tt&&sumc[q[tt]]>=sumc[i]) --tt; 27 q[++tt] = i; 28 if(i-len+1>0&&sumc[q[hh]]-sumc[i-len]>=0) can[i-len+1] = 1; 29 } 30 hh = 0; tt = -1; 31 for(int i=1;i<=2*len;i++) 32 sumc[i] = sumc[i-1]+a[2*len-i]; 33 for(int i=1;i<2*len;i++){//反向求的最小和是反向前缀和的最小,与正向不同 34 while(hh<=tt&&i-len+1>q[hh]) ++hh; 35 while(hh<=tt&&sumc[q[tt]]>=sumc[i]) --tt; 36 q[++tt] = i; 37 if(i-len+1>0&&sumc[q[hh]]-sumc[i-len]>=0){ 38 can[len*2-i] = 1;//从上面可判断i一定大于len-1 这个起点已经转化成还未破环的坐标 39 printf("%d\n",len*2-i) ; 40 } 41 } 42 for(int i=1;i<=len;i++) 43 if(can[i]) cnt++; 44 printf("Case %d: %d\n",++kcase,cnt); 45 } 46 return 0; 47 }