题目大意
给定两个字符串s和t,长度为n,你可以对一个字符串其中长度为l的一段进行翻转,l取值为1…n,但同时也必须对另一个串选择长度为l的一段进行翻转,这两个翻转的起点终点可以不一样。问你是否有可能在经过若干次操作后使得两个串变得相同。可能输出YES,否则输出NO。
结论
先上结论。
如果两个串对应字符数量不一致那么肯定不行,输出NO;
如果数量一致并且s串中有相同字母,那么一定可以,输出YES。(t串也一样,俩串字母一样的)
如果木有相同字母,但是s串和t串的逆序对数的和为偶数,那么一定可以,输出YES。
如果连和都不是偶数就不行,输出NO。
我的思维
第一步,试着简化,看是否能只让一个串变化。
我们记一个串是
,另一个为
,假设两个串经过x次操作可以变成相同的串
,那么可以看成是
先经过了
次翻转变成s,再经过
次操作变成
。这个等价过程中要保持操作长度的一致性:假设x=3次操作长度依次为5,3,6,那么剩下3次操作长度依次必须是6,3,5,总操作长度组成的序列就是5,3,6,6,3,5。
第二步,特殊化,既然翻转长度l是可以任意选的,那么我们可以发现一切的翻转操作都可以等价于若干个长度为2的翻转操作,所以我们可以限定操作长度只能是2。
第三步,长度为2的规定套入第一步,就会发现我们只要能经过偶数次长度为2的操作能将
变为
,那么就输出YES。
不过想到这一步会发现还过不了一个样例。
:ababa
:baaba
我们对s2做了一次翻转操作两者就相同了,次数是奇数,于是输出NO,然而答案是YES。
这是因为
串中含有相邻的相同字母aa,于是我们就可以爱对它们两个做几次翻转就做几次翻转,就可以把操作次数化奇为偶。
但是这个情况还不够一般,有可能原来的
、
串都没有相邻的相同字母,但是经过翻转以后两个相同的字母可能会靠在一起。
我们考虑
如果里面含有两个相同的字母,但是不相邻。我们试着让后面那个向前移动(就是一直和前一个交换)。若第一个出现在pos1,第二个出现在pos2,那么第二个向前移动最少pos2-pos1-1次就可以使得两个相同字母相邻。
既然我们对
做了这么多次操作,我们也得对
做pos2-pos1-1次操作,为了方便描述将这个次数记为tmp。我们对
中随意选择两个相邻元素进行tmp次交换,会发现若tmp是偶数,相当于没交换,否则相当于交换。
如果tmp能是偶数就好了,所以灵机一动又可以想到s1中那两个字母既然已经相邻了,那我们可以利用他们两个化奇为偶。
于是我们得出结论:可以在不改变另一个串的情况下,将一个串的两个不相邻相同字母翻转至相邻。
所以只要有相同字母就可以相邻,然后我们就利用他们化奇为偶,从而保证了
串可以通过偶数次长度为2的翻转操作变为
串。
那么如果没有相同字母呢?
我们就以
为“模板”,每次在
里找到对应字母变换到对应位置。
【注意我们一直使用长度为2的翻转】
比如说
,
第一步要做的就是
第一个位置搞成
接下来第二、第三个位置依次是
然后统计一下操作次数
是不是偶数,是偶数输出YES,否则NO。
至此我们的问题就已经解决啦,但是我们还有更优雅的办法。
上面这种计算
的方法要写三重循环(一重扫描
,第二重扫描
,第三重交换),不够快乐。
这里我们再利用一种思维——标准化思维
我们把
串先经过
操作变为升序序列,再以这个升序序列为起点经过
变化变为
串,两者的和就是总的操作次数。
你问这个有什么好处吗?
有哒,你应该已经可以看出来
其实就是
串的逆序对数,
同理。那我们就只使用两重循环计算逆序对就可以了,写代码时免去考虑交换的细节,代码复杂度比较低。
最后再扯一下时间复杂度, 计算逆序对是没问题的,不会超时。如果串长超26,必然有重复字母,不会到计算逆序对这一步。因此我们计算的串长最长也就26,总串长20W,就算有1W个都是长度为26的那 完全可以接受。
上代码
代码比较丑,欢迎指正。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int (i)=(l);(i)<=(r);(i)++)
#define per(i,l,r) for(int (i)=(r);(i)>=(l);(i)--)
using namespace std;
const int LEN=2e5+10;
int q,s1[LEN],s2[LEN],count1[27],count2[27];
int nixu(int s[],int len){
int cnt=0;
rep(i,2,len)
rep(j,1,i-1)
if(s[i]<s[j])
cnt++;
return cnt;
}
int main(){
scanf("%d",&q);
while(q--){
int n;
bool cnt_judge=true,repeat_judge=false;
memset(count1,0,sizeof(count1));
memset(count2,0,sizeof(count2));
scanf("%d",&n);
getchar();
rep(i,1,n){
s1[i]=getchar()-'a'+1;
if((++count1[s1[i]])>1)
repeat_judge=true;
}
getchar();
rep(i,1,n){
s2[i]=getchar()-'a'+1;
count2[s2[i]]++;
}
rep(i,1,26)
if(count1[i]!=count2[i])
cnt_judge=false;
if(cnt_judge==false)
printf("NO\n");
else if(repeat_judge)
printf("YES\n");
else if(((nixu(s1,n)+nixu(s2,n))&1)==0)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}