Codeforces Round #598 (Div. 3) F
首先无论反转多长的子串 最后的效果能够等同于进行若干次相邻两个字母交换
把相邻交换看作后者前移
abcd->abdc->adbc->dabc
dabc->dacb->dcab
dcab->dcab
dcba
接下来分类讨论
- 如果有字母在S和T中出现次数不同
显然 (sf{NO}) 这还需要解释吗
- 如果S和T中存在一个字母出现不止一次
把 S 中这两个字母想办法变成相邻 这一定能做到
然后每次操作交换 S 的这两个相邻字母 T就可以任意交换
S 不变 T 一定可以变成 S
因为可以看作每一个字符任意右移 每次把需要移动的移过去就行了
- 其它情况
我们可以先想办法把 S 变成升序 同时对T任意选择两个相邻字母每次交换这两个 这一步的次数取决于 S 中逆序对的个数 假设这个数为(x)
这样之后再把 T 变成升序 假设 T 中逆序对个数为(y) 这一步需要的步数等于(y+1)或(y-1)((x)为奇数) 或 (y)((x)为偶数)
如果(x)为奇数 在"任意选择两个相邻字母每次交换这两个"操作中 S排序完后T的逆序对显然会增加或减少一个 而如果(x)为偶数 这个数不会改变
同时也要在S上进行这些操作(任意选择两个相邻字母每次交换这两个)
可以看出(x+y)为偶数是能做到上面一步 否则如果一个为升序 另一个逆序对必会为1
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
LL T,n;
string s,t;
LL cnt[2][28] = {0};
LL tr[35] = {0};
LL lowbit(LL x){ return (x & (-x)); }
void modify(LL x){ for(;x <= 26;x += lowbit(x)) tr[x] ++; }
LL query(LL x){ LL ret = 0; for(;x;x -= lowbit(x)) ret += tr[x]; return ret; }
// 树状数组求逆序对
// 好像没必要啊
int main(){
cin >> T;
while(T --){
memset(cnt,0,sizeof(cnt));
cin >> n; cin >> s >> t;
for(LL i = 0;i < n;i ++){
cnt[0][s[i] - 'a' + 1] ++;
cnt[1][t[i] - 'a' + 1] ++;
}
LL dif = 0; for(LL i = 1;i <= 26;i ++) if(cnt[0][i] != cnt[1][i]) dif = 1;
if(dif){ cout << "NO" << endl; continue; }
// 情况1
LL sam = 0; for(LL i = 1;i <= 26;i ++) if(cnt[0][i] >= 2) sam = 1;
if(sam){ cout << "YES" << endl; continue; }
// 情况2
LL ans = 0,ant = 0;
memset(tr,0,sizeof(tr));
for(LL i = 0;i < n;i ++){ ans += query(s[i] - 'a' + 1); modify(s[i] - 'a' + 1); }
memset(tr,0,sizeof(tr));
for(LL i = 0;i < n;i ++){ ant += query(t[i] - 'a' + 1); modify(t[i] - 'a' + 1); }
cout << ((ans & 1) == (ant & 1) ? "YES" : "NO") << endl;
}
return 0;
}