题意:输入一个长度为n的字符串,由R、S、P三种字符组成。分别代表石头、剪刀、布。每一轮游戏,从左往右遍历,每相邻的两位进行游戏,若第i位能赢过第i+1位(石头剪刀布的规则),那么两位互换,遍历字符串一遍算一轮游戏。输出原字符串进行T轮游戏后的结果。
因为n,T的范围都很大,模拟肯定是不现实的。所以,可以想想O(n)的做法。三个字母都在的情况很难考虑,那就简化问题。在一个字符串中,如果只有两种字母,那么显然,在游戏进行足够多轮后,二者之中的败者会在字符串的前部,胜者会在字符串的后部。然后就要证明,不同段只含两个字母的字符串之间不会互相影响。显然当一串字符串中出现第三种字符时,因为前二者的胜者在后,显然,前二者的胜者不可能胜过第三种字符。比如,RS的字符串,结尾是R,R不能胜过P,因此,不同段的字符串之间不会互相影响,不会出现跨区的交换。
因此,该题可以简化为多段只含两个字母的字符串的问题。而对于每一段中的败者,假设他在该段的第i位,是该段中第k个败者,那么他的位置为max(k,i-T);O(n)时间能解决该题。
#include<bits/stdc++.h>
using namespace std;
char st[503500];
int a[503500],p[5],pos,lossf,winf,cnt;
long long n,T;
bool f[10];
long long max0(long long x,long long y)
{
if (x>y) return x;
return y;
}
void swap(int x,int y)
{
int t;
t=a[x];a[x]=a[y];a[y]=t;
}
int main()
{
scanf("%lld%lld",&n,&T);
scanf("%s",st+1);
for (int i=1;i<=n;i++)
{
if (st[i]=='R') a[i]=1;
if (st[i]=='S') a[i]=2;
if (st[i]=='P') a[i]=3;
}
f[1]=true;f[2]=true;f[3]=true;
f[a[1]]=false;cnt=1;
int l=1;
for (int i=2;i<=n;i++)
{
if (f[a[i]])
{
if (cnt<=1)
{
cnt++;
p[cnt]=a[i];
}
else
{
if (f[3]) {lossf=2;winf=1;}
if (f[1]) {lossf=3;winf=2;}
if (f[2]) {lossf=1;winf=3;}
int k=0;
for (int j=l;j<=i-1;j++)
{
if (a[j]==lossf)
{
k++;pos=max0(l+k-1,j-T);
swap(j,pos);
}
}
f[1]=true;f[2]=true;f[3]=true;
cnt=1;
p[cnt]=a[i];
l=i;
}
f[a[i]]=false;
}
}
if (cnt==2)
{
if (f[3]) {lossf=2;winf=1;}
if (f[1]) {lossf=3;winf=2;}
if (f[2]) {lossf=1;winf=3;}
int k=0;
for (int j=l;j<=n;j++)
{
if (a[j]==lossf)
{
k++;pos=max0(l+k-1,j-T);
swap(j,pos);
}
}
}
for (int i=1;i<=n;i++)
{
if (a[i]==1) printf("R");
if (a[i]==2) printf("S");
if (a[i]==3) printf("P");
}
return 0;
}