F. Equalizing Two Strings(Codeforces Round #598 (Div. 3))(思維-模擬)
time limit per test:1 second
memory limit per test:256 megabytes
input:standard input
outputs:tandard output
Description
You are given two strings and both of length and both consisting of lowercase Latin letters.
In one move, you can choose any length len from 1 to n and perform the following operation:
- Choose any contiguous substring of the string of length len and reverse it;
- at the same time choose any contiguous substring of the string of length len and reverse it as well.
Note that during one move you reverse exactly one substring of the string s and exactly one substring of the string .
Also note that borders of substrings you reverse in and in can be different, the only restriction is that you reverse the substrings of equal length. For example, if and , you can reverse and and , but not and .
Your task is to say if it is possible to make strings and equal after some (possibly, empty) sequence of moves.
You have to answer independent test cases.
Input
The first line of the input contains one integer — the number of test cases. Then test cases follow.
The first line of the test case contains one integer — the length of and .
The second line of the test case contains one string consisting of lowercase Latin letters.
The third line of the test case contains one string consisting of lowercase Latin letters.
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, print the answer on it — "YES"
(without quotes) if it is possible to make strings and equal after some (possibly, empty) sequence of moves and "NO"
otherwise.
input
4
4
abcd
abdc
5
ababa
baaba
4
asdf
asdg
4
abcd
badc
output
NO
YES
NO
YES
題意
給你兩個字符串 和 ,長度都是 ,都由小寫拉丁字母組成。
在一次移動中,您可以選擇從 到 的任意長度 並執行以下操作:
選擇長度爲 的字符串 的任何相鄰子字符串並將其反轉;
同時選擇 長度的字符串 的任何相鄰子字符串,並將其反轉。
注意,在一個移動過程中,正好反轉字符串 的一個子字符串和字符串 的一個子字符串。
還要注意,在 和 中反轉的子串的邊界可以不同,唯一的限制是反轉長度相等的子串。例如,如果 和 ,則可以反轉 和 和 ,但不能反轉 和 。
你的任務是說在一些(可能是空的)移動序列之後是否可以使字符串 和 相等。
——來自百度翻譯
題解
首先要明白兩幾個規律:
- 把 和 分別挑一段倒置相當於把 或 單獨倒置兩次。
- 倒置偶數次相同的位置相當於沒有倒置,而且倒置奇數次同一段子串相當於把那一段子串倒置 次,所以題目就要求要分別挑兩段不同的子串倒置
- 長度爲奇數的倒置可以轉化成奇數次相鄰兩個元素的調換,長度爲偶數的倒置可以轉換成偶數次相鄰元素的調換;例如
abc
轉化成cba
需要 次相鄰元素的調換位置 ,abcd
轉化成dcba
需要 次相鄰元素的調換位置 。那麼題目要求就可以轉化成偶數次相鄰元素的調換位置
那麼現在題目就變成了讓字符串 經過偶數次相鄰元素的調換能不能轉化成字符串 。
現在還可以先篩選出一些確定的情況:
- 若 中的各個元素的個數和 中的不一樣的話是肯定不可能轉換成功的。
- 若 或 中有某個元素的個數多於一個的話是肯定能轉換成功的。因爲這時可以先把 中的這兩個元素調換在一起,然後就可以反覆調換 中的這兩個元素的位置,而在 字符串中調換其他的元素,那就相當於不斷變換 的位置,那 當然能變換成任意順序,最終達到和 相同。
若此時還沒有判斷出是否可能,那麼此時 和 就滿足裏面的每個元素都不同且兩個字符串的元素存在一一對應的關係。
那麼就開始遞推:
從 中的第一個元素開始,查找 中的對應的元素,假設和 對應的是 ,那麼就調換 和 的位置。不過調換前需要判斷 是不是偶數,若不是,需要先調換一下 和 (之所以不能調換 中的元素,是因爲不能確定調換哪兩個對 無影響,若是能找到不影響 的,那也可以,總之調換必須是偶數次就行了),這樣就能使 和 是一樣的。
循環如此,之後就只需要判斷最後兩個是否相同就可以判斷能否轉化了。因爲如果只有最後兩個不同,那麼只有轉換奇數次才能相同。
代碼
#include<bits/stdc++.h>
using namespace std;
const int MAXA = 4e5;
int n, a[MAXA], b[MAXA], tmp, j;
char s1[MAXA], s2[MAXA];
int solve() {
scanf("%d", &n);
scanf("%s", s1 + 1);
scanf("%s", s2 + 1);
for (int i = 0; i < 26; i++) a[i] = b[i] = 0;
for (int i = 1; i <= n; i++) {
a[s1[i] - 'a']++;
b[s2[i] - 'a']++;
}
for (int i = 0; i < 26; i++)
if (a[i] != b[i])
return printf("NO\n"), 0;
for (int i = 0; i < 26; i++)
if (a[i] > 1 || b[i] > 1)
return printf("YES\n"), 0;
if (n == 1) return printf("YES\n"), 0;
for (int i = 1; i <= n - 2; i++) {
if (s1[i] == s2[i])
continue;
for (j = i + 1; j <= n; j++)
if (s1[i] == s2[j])
break;
if ((j - i) & 1) swap(s1[i + 1], s1[i + 2]);
tmp = s2[j];
for (; j > i; j--)
s2[j] = s2[j - 1];
s2[i] = tmp;
}
if (s1[n] == s2[n])
printf("YES\n");
else
printf("NO\n");
return 0;
}
int main() {
//freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while (t--)
solve();
}