- 给定一个长度为\(n\)的由
L
和R
构成的字符串\(s\)。 - 求构造一个排列\(p_i\),满足\(\forall i\in[1,n),s[p_i]\not=s[p_{i+1}]\),且满足\(p_i>p_{i+1}\)的\(i\)的个数最小。
- \(n\le10^5\)
可以证伪的贪心
考虑一个贪心,就是每次尽可能选取长度最大的符合条件的子序列,然后重复取若干次直至把整个序列取完。
这一过程的实现是非常简单的,只要对L
和R
两种字符各开一个\(set\),每次\(lower\_bound\)找后继就好了。
然而,这个贪心是会被卡掉的。
唯一一种\(Hack\)数据
会被卡掉是毋庸置疑的,但能卡掉这个贪心的数据实际上只有一种。
以L
为例,如果它选中的R
后面全都是R
,且在此之前还有R
没有匹配,这时候贪心就会有问题。
因为根据贪心的思路我们会先匹配后面的R
再折回去找之前的L
和R
,那么不如先回去匹配前面的R
再一路匹配下来。
只要特判一下这种情况就可以了。
代码:\(O(nlogn)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,v[N+5];char s[N+5];set<int> S[2];
I int Solve(RI x)//第一个字符为x
{
RI i,t=0;for(i=1;i<=n;++i) S[s[i]=='R'].insert(i);//分字符插入set
#define Nxt(x,p) S[x].lower_bound(p)//后继
#define Mn(x) *S[x].begin()//最小值
RI p=0;for(i=1;i<=n;S[x].erase(v[i]=p),++i,x^=1)
p=Nxt(x,p)==S[x].end()?(++t,Mn(x)):*Nxt(x,p),//找到下一位
Nxt(x^1,p)==S[x^1].end()&&p^Mn(x)&&Mn(x)<Mn(x^1)&&(p=Mn(x),++t);//如果之后全是同样的字符,不如先选前面的
return t;
}
int main()
{
RI i,t=0;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) s[i]=='L'?++t:--t;//比较两种字符谁更多
for(printf("%d\n",t?Solve(t==-1):Solve(s[1]=='R')),i=1;i<=n;++i) printf("%d ",v[i]);return 0;
}