这题有一个显而易见的结论:将一个串划分成若干个美丽的子串,用最少的次数把这些串取完则是最优方案。可以证明不会有比这种取法更优的方案。
更形式地描述,将一个串划分成若干个子串的过程,就是找到所有 (a_i=a_{i+1}) 的 (i)。对于第 (x) 个这样的 (i),将其记为 (p_x)。那么从左向右数的第 (x) 个美丽的子串就为 (a[p_{x-1}+1..p_x])。
经过手玩之后我们可以发现,如果把每个美丽的子串用它的最后一个字母代表,构成一个新串 (b),即(b_i=a_{p_i})那么在原串 (a) 上 取一个美丽的子串的操作就变为下述两种操作:
- 在新串 (b) 上取一个字符。
- 在新串 (b) 上取两个 相邻但不相同 的字符。
怎么理解这样的两个操作呢?
对于第一种操作,分两种情况讨论:
- 在原串上取了头尾的子串,显然在 (b) 上的影响为被取走一个字符。
- 在原串上取了中间的子串,但是对于被取走的子串 (a[p_{i-1}+1,p_i]),有 (a_{p_{i-1}}=a_{p_i+1})。经过分析很容易得到,这就是第二个操作去掉 不相同约束 的情况。
经过上述讨论自然可以得出, (b) 上的一次上述操作对应原串 (a) 上的一次操作。
那么问题就转化为:在一个字符串上,每次可以取走一个字符,或者 取走两个相邻但不相同 的字符,每次进行一次操作后,将剩余的字符按原来顺序连接起来,求最少多少次能把这个字符串取空。
依然是分类讨论,记 (b) 中最多出现的字符次数为 (mx),(b) 的长度为 (sum),则:
-
若 (frac{mx}{2}>sum),由于第二个操作更优,尽可能使用第二个操作令其他字符与最多出现的字符配对,如果不再有字符能与最多出现的字符配对,则将剩下的字符全部使用第一个操作取完。
-
若 (frac{mx}{2}leq sum),由于第二个操作更优,我们会尽可能使用第二个操作,令不相同的两个字符配对。直到当前的 (frac{mx}{2}>sum),转化为第一种情况。
经过手玩可以发现上述过程与括号匹配很像,可以使用栈实现上述过程。
(b) 串中默认忽略了最后一个子串,所以最后 (ans=max(lceilfrac{sum}{2} ceil,mx)+1),还需要最后一次删除将最后那个串删掉。
由于最后输出方案时需要输出每次删除后的位置而不是初始位置,可以使用线段树维护,区间赋值+区间查询+单点查询即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mx tot[res]
#define max(a,b) ((a)>(b)? (a):(b))
#define min(a,b) ((a)<(b)? (a):(b))
struct node {int l,r;}tmp[200005];
char a[200005],b[200005];
int tag[800005],sum[800005];
int tot[355],resL[200005],resR[200005],st[200005],p[200005],tmpL[200005],tmpR[200005];
inline bool cmp1(node x,node y) {return x.l<y.l;}
inline bool cmp2(node x,node y) {return x.r>y.r;}
inline void build(int p,int l,int r) {
tag[p]=-1;sum[p]=0;
if(l==r) {sum[p]=1;return;}
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
sum[p]=sum[p<<1]+sum[p<<1|1];
}
inline void spread(int p,int l,int r) {
int mid=l+r>>1;
if(tag[p]!=-1) {
tag[p<<1]=tag[p<<1|1]=tag[p];
sum[p<<1]=(mid-l+1)*tag[p];
sum[p<<1|1]=(r-mid)*tag[p];
tag[p]=-1;
}
}
inline void change(int p,int l,int r,int L,int R,int val) {
if(L<=l&&r<=R) {sum[p]=(r-l+1)*val;tag[p]=val;return;}
spread(p,l,r);
int mid=l+r>>1;
if(L<=mid) change(p<<1,l,mid,L,R,val);
if(R>mid) change(p<<1|1,mid+1,r,L,R,val);
sum[p]=sum[p<<1]+sum[p<<1|1];
}
inline int ask(int p,int l,int r,int L,int R) {
if(L<=l&&r<=R) return sum[p];
spread(p,l,r);
int mid=l+r>>1,res=0;
if(L<=mid) res+=ask(p<<1,l,mid,L,R);
if(R>mid) res+=ask(p<<1|1,mid+1,r,L,R);
return res;
}
inline int askPos(int p,int l,int r) {
if(l==r) return l;
int mid=l+r>>1;
if(sum[p<<1]) return askPos(p<<1,l,mid);
else return askPos(p<<1|1,mid+1,r);
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
int top=0,m=0,num=0,len=0,ans=0,res=0,now,pos1,pos2;
scanf("%s",a+1);
len=strlen(a+1);
for(register char ch='a';ch<='z';++ch) tot[ch]=0;
for(register int i=1;i<=len;++i) if(a[i]==a[i+1]) {++tot[a[i]];p[++num]=i;b[num]=a[i];}
m=num;pos1=1;pos2=len;
//for(register int i=1;i<=num;++i) printf("%d
",p[i]);
build(1,1,len);
//std::swap(b[num],b[num-1]);std::swap(p[num],p[num-1]);
//if(num&1) {resL[++ans]=1;resR[ans]=p[1];tmpL[ans]=1;tmpR[ans]=p[1];change(1,1,len,1,p[1],0);--tot[b[1]];--m;}
//if(num&1) {resL[++ans]=p[num]+1;resR[ans]=len;tmpL[ans]=p[num]+1;tmpR[ans]=len;change(1,1,len,p[num]+1,len,0);--tot[b[num]];--num;--m;}
//if(num&1) {resL[++ans]=p[num-1]+1;resR[ans]=p[num];tmpL[ans]=resL[ans];tmpR[ans]=resR[ans];change(1,1,len,resL[ans],resR[ans],0);--tot[b[num-1]];--m;}
for(now=1;now<=num;++now) {
//if(now==num-1) continue;
for(register char ch='a';ch<='z';++ch) res=tot[res]>tot[ch]? res:ch;
if(2*mx>=m) break;
if(!top) {st[++top]=now;}
else {
if(b[st[top]]==b[now]) {st[++top]=now;}
else {
--tot[b[st[top]]];--tot[b[now]];
resL[++ans]=ask(1,1,len,1,p[st[top]]+1);resR[ans]=ask(1,1,len,1,p[now]);
tmpL[ans]=p[st[top]]+1;tmpR[ans]=p[now];
change(1,1,len,p[st[top]]+1,p[now],0);--top;m-=2;
}
}
}
if(m) {
for(;now<=num;++now) {
if(!top||b[st[top]]==b[now]||(b[st[top]]!=res&&b[now]!=res)) {st[++top]=now;}
else {
--tot[b[st[top]]];--tot[b[now]];
resL[++ans]=ask(1,1,len,1,p[st[top]]+1);resR[ans]=ask(1,1,len,1,p[now]);
tmpL[ans]=p[st[top]]+1;tmpR[ans]=p[now];
change(1,1,len,p[st[top]]+1,p[now],0);--top;m-=2;
}
}
}
for(register int t=1;t<=top;++t) {
int pos=askPos(1,1,len);
--tot[b[st[t]]];
resL[++ans]=ask(1,1,len,1,pos);
resR[ans]=ask(1,1,len,1,p[st[t]]);
tmpL[ans]=pos;tmpR[ans]=p[st[t]];
change(1,1,len,pos,p[st[t]],0);
}
printf("%d
",ans+1);
for(register int i=1;i<=ans;++i) {printf("%d %d
",resL[i],resR[i]);tmp[i].l=tmpL[i];tmp[i].r=tmpR[i];}
//for(register int i=1;i<=ans;++i) {printf("%d %d
",tmpL[i],tmpR[i]);tmp[i].l=tmpL[i];tmp[i].r=tmpR[i];}
//pos1=(resL[i]<=pos1? max(pos1,resR[i]+1):pos1);(pos2=resR[i]>=pos2? min(pos2,resL[i]-1):pos2);
std::sort(tmp+1,tmp+1+ans,cmp1);
for(register int i=1;i<=ans;++i) pos1=(tmp[i].l<=pos1? max(pos1,tmp[i].r+1):pos1);
std::sort(tmp+1,tmp+1+ans,cmp2);
for(register int i=1;i<=ans;++i) pos2=(tmp[i].r>=pos2? min(pos2,tmp[i].l-1):pos2);
pos1=max(pos1,1);pos2=min(pos2,len);
//printf("%d %d
",pos1,pos2);
//for(register int i=1;i<=ans;++i) printf("%d %d
",resL[i],resR[i]);
printf("%d %d
",ask(1,1,len,1,pos1),ask(1,1,len,1,pos2));
}
return 0;
}