注:本题最初来源是CF578E Walking,后被选入IOI2020国家队作业,之后又出现在六校联考 #33这场模拟赛中。
首先,将整个串划分为,尽可能少的、( ext{L}, ext{R})交替的子序列(不一定连续),再考虑将这些子序列拼起来。设划分出了(k)个子序列,则答案的下界显然是(k-1)。确定了一种划分方法后,我们要做的,是构造一种拼接这些子序列的方法,使答案取到这个下界。
划分,可以贪心解决。从前向后依次考虑(S)的每个位置。维护前面已经划分出的所有子序列。如果有末尾字母和(S)当前位置上字母不同的子序列,就把当前位置加入到这个子序列中,否则就令当前位置单独作为一个子序列。这样就愉快地划分好了(正确性请感性理解)。
接下来要构造拼接方案。我们把划分出的这些子序列,按照首、尾字母,分为:( ext{LL}), ( ext{RR}), ( ext{LR}), ( ext{RL})四类。
因为题目保证了( ext{L}, ext{R})的总数相差不超过(1),所以( ext{LL}), ( ext{RR})两类子序列的数量相差不超过(1)。考虑先将这两类子序列拼接起来:
- 如果( ext{LL}), ( ext{RR})数量相等,则可以得到一个大的,形如:( ext{LR})的“子序列”。注意,这里打引号的“子序列”,意思是它实际上并不真的是原序列的一个子序列,因为在拼接时,会有“跳回去”(也就是“倒着走”)的情况。但这个“子序列”,代表的含义是连续的一段脚印,所以在构造方案时,它和真正的子序列没有区别。在下面的表述中,我们会用引号和斜体标注这种等价的“子序列”,如果未被标注,则表示通常意义上的子序列。
- 如果( ext{LL})比( ext{RR})多,则可以得到一个形如( ext{LL})的“子序列”。
- 如果( ext{RR})比( ext{LL})多,则可以得到一个形如( ext{RR})的“子序列”。
经过这一番拼接,我们手上,( ext{LL}), ( ext{RR})两种“子序列”,最多只剩一个。
再考虑拼接( ext{LR}), ( ext{RL})这两类“子序列”。我们首先可以把所有( ext{LR})拼成一个大的( ext{LR}),也可以把所有( ext{RL})拼成一个大的( ext{RL})。现在我们手上最多只剩一个( ext{LR})和一个( ext{RL})。怎样把它们拼在一起呢?比较棘手。这也是本题的难点。
考虑( ext{LR})和( ext{RL})的最后一个位置(是脚印序列的最后一个脚印,但并不一定是“子序列”中坐标最大的位置,因为“子序列”可能存在“跳回去”的情况),分别记为(p), (q)。不妨假设(p<q)。那么,我们可以把(q)这个位置上的( ext{L}),接到( ext{LR})序列的末尾,再把( ext{RL})序列,除了(q)以外的其它部分,(从前往后)依次接在后面。这样,我们就用(1)次“跳回去”的操作,成功合并了( ext{LR})和( ext{RL})两个“子序列”。
现在,我们手上,( ext{LR}), ( ext{RL})两种“子序列”,最多只剩一个。
综上,在最后,我们手上的子序列,有如下几种可能:
- 只剩一个“子序列”了。那么它的长度必为(n),直接把这个“子序列”输出即可。
- 剩( ext{LL}), ( ext{RR})两种中的一个,和( ext{LR}), ( ext{RL})两种中的一个。分四类情况讨论一下,在满足( ext{L}, ext{R})交替的前提下,将它们拼接即可。
时间复杂度,(O(|S|))。
参考代码:
//problem:nflsoj621
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int MAXN=1e5;
int n,cnt;
char s[MAXN+5];
vector<int>vL,vR,v[MAXN+10];
vector<int>LL,RR,LR,RL;
int main() {
cin>>(s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i){
if(s[i]=='L'){
if(!SZ(vR)){
++cnt;
vL.pb(cnt);
v[cnt].pb(i);
}
else{
vL.pb(vR.back());
v[vR.back()].pb(i);
vR.pop_back();
}
}
else{
if(!SZ(vL)){
++cnt;
vR.pb(cnt);
v[cnt].pb(i);
}
else{
vR.pb(vL.back());
v[vL.back()].pb(i);
vL.pop_back();
}
}
}
cout<<cnt-1<<endl;
for(int i=1;i<=cnt;++i){
int st=v[i][0],ed=v[i].back();
if(s[st]=='L' && s[ed]=='L')LL.pb(i);
if(s[st]=='R' && s[ed]=='R')RR.pb(i);
if(s[st]=='L' && s[ed]=='R')LR.pb(i);
if(s[st]=='R' && s[ed]=='L')RL.pb(i);
}
if(SZ(LL) && SZ(RR)){
if(SZ(LL)==SZ(RR)){
++cnt;
LR.pb(cnt);
for(int i=0;i<SZ(LL);++i){
for(int j=0;j<SZ(v[LL[i]]);++j)v[cnt].pb(v[LL[i]][j]);
for(int j=0;j<SZ(v[RR[i]]);++j)v[cnt].pb(v[RR[i]][j]);
}
vector<int>().swap(LL);
vector<int>().swap(RR);
}
else if(SZ(LL)>SZ(RR)){
assert(SZ(RR)==SZ(LL)-1);
++cnt;
for(int i=0;i<SZ(RR);++i){
for(int j=0;j<SZ(v[LL[i]]);++j)v[cnt].pb(v[LL[i]][j]);
for(int j=0;j<SZ(v[RR[i]]);++j)v[cnt].pb(v[RR[i]][j]);
}
int i=SZ(RR);
for(int j=0;j<SZ(v[LL[i]]);++j)v[cnt].pb(v[LL[i]][j]);
vector<int>(1,cnt).swap(LL);
vector<int>().swap(RR);
}
else{
assert(SZ(LL)==SZ(RR)-1);
++cnt;
for(int i=0;i<SZ(LL);++i){
for(int j=0;j<SZ(v[RR[i]]);++j)v[cnt].pb(v[RR[i]][j]);
for(int j=0;j<SZ(v[LL[i]]);++j)v[cnt].pb(v[LL[i]][j]);
}
int i=SZ(LL);
for(int j=0;j<SZ(v[RR[i]]);++j)v[cnt].pb(v[RR[i]][j]);
vector<int>().swap(LL);
vector<int>(1,cnt).swap(RR);
}
}
assert(SZ(LL)<=1);assert(SZ(RR)<=1);assert(!(SZ(LL)==1 && SZ(RR)==1));
if(SZ(LR)>1){
++cnt;
for(int i=0;i<SZ(LR);++i){
for(int j=0;j<SZ(v[LR[i]]);++j)v[cnt].pb(v[LR[i]][j]);
}
vector<int>(1,cnt).swap(LR);
}
if(SZ(RL)>1){
++cnt;
for(int i=0;i<SZ(RL);++i){
for(int j=0;j<SZ(v[RL[i]]);++j)v[cnt].pb(v[RL[i]][j]);
}
vector<int>(1,cnt).swap(RL);
}
assert(SZ(LR)<=1);assert(SZ(RL)<=1);
if(SZ(LR) && SZ(RL)){
if(v[LR[0]].back() > v[RL[0]].back()){
v[RL[0]].pb(v[LR[0]].back());
for(int i=0;i<=SZ(v[LR[0]])-2;++i)
v[RL[0]].pb(v[LR[0]][i]);
vector<int>().swap(LR);
}
else{
v[LR[0]].pb(v[RL[0]].back());
for(int i=0;i<=SZ(v[RL[0]])-2;++i)
v[LR[0]].pb(v[RL[0]][i]);
vector<int>().swap(RL);
}
}
#define printv(v) do{for(int i=0;i<SZ((v));++i)cout<<(v)[i]<<" ";}while(0)
if(!SZ(LL) && !SZ(RR)){
if(SZ(LR))printv(v[LR[0]]);
if(SZ(RL))printv(v[RL[0]]);
}
else if(!SZ(LR) && !SZ(RL)){
if(SZ(LL))printv(v[LL[0]]);
if(SZ(RR))printv(v[RR[0]]);
}
else if(SZ(LL) && SZ(LR)){
printv(v[LR[0]]);printv(v[LL[0]]);
}
else if(SZ(LL) && SZ(RL)){
printv(v[LL[0]]);printv(v[RL[0]]);
}
else if(SZ(RR) && SZ(LR)){
printv(v[RR[0]]);printv(v[LR[0]]);
}
else if(SZ(RR) && SZ(RL)){
printv(v[RL[0]]);printv(v[RR[0]]);
}
else assert(0);
cout<<endl;
return 0;
}