题目大意:有一个长度为n的字符串,每一位只会是p或j。求一个最长子串,使得不管是从左往右还是从右往左取,都保证每时每刻已取出的p的个数不小于j的个数。
首先先扫一遍得到以每个点为左端点,只考虑从左向右取,最远能取到哪,并标记每一个点最早被哪一个点扫到
然后再从后往前扫一遍,开两个栈,一个记录当前仍符合条件的右端点,一个记录已经被扫过的左端点,每当有元素从第一个栈里弹出时,就在第二个栈里二分找出符合两个条件的最远的左端点
#include<iostream>
#include<cstdio>
#define N 1000010
using namespace std;
char c[N];
int pre[N];
int s[N],t;
int fir[N],far[N];
int b[N],p;
int main()
{
int n;
scanf("%d",&n);
int i,j;
scanf("%s",c+1);
for(i=1;i<=n;i++)
{
if(c[i]=='p')
pre[i]=pre[i-1]+1;
else pre[i]=pre[i-1]-1;
}
for(i=1;i<=n;i++)
{
while(t&&pre[i]-pre[s[t]-1]<0)
{
far[s[t]]=i-1;
t--;
}
if(t) fir[i]=s[1];
else if(pre[i]-pre[i-1]==1) fir[i]=i;
else fir[i]=707185547;
if(pre[i]-pre[i-1]==1) t++,s[t]=i;
}
while(t)
{
far[s[t]]=n;
t--;
}
int ans=0;
int L,R,mid;
for(i=n;i>=1;i--)
{
while(t&&pre[s[t]]-pre[i-1]<0)
{
L=2;R=p+1;
while(L<R)
{
mid=(L+R)>>1;
if(far[b[mid]]>=s[t]) L=mid+1;
else R=mid;
}
ans=max(ans,s[t]-max(fir[s[t]]-1,b[L-1]-1));
t--;
}
if(pre[i]-pre[i-1]==1)
{
t++;s[t]=i;
while(p&&far[b[p]]<=far[i]) p--;
p++;b[p]=i;
}
}
while(t)
{
L=2;R=p+1;
while(L<R)
{
mid=(L+R)>>1;
if(far[b[mid]]>=s[t]) L=mid+1;
else R=mid;
}
ans=max(ans,s[t]-max(fir[s[t]]-1,b[L-1]-1));
t--;
}
printf("%d",ans);
}