文章目录
- [ Unforgivable Curse (hard version)](https://codeforces.com/contest/1800/problem/E2)
- 问题建模
- 问题分析
- 方法1分析性质
- 1.分析操作对元素位置的影响
- 2.分析可以使用操作的元素可以与相邻元素交换位置的作用
- 代码
- 方法2通过DFS得到相互可以交换位置的字符集合
- 代码
- 方法3通过并查集连通可交换位置
- 代码
Unforgivable Curse (hard version)
问题建模
给定两个字符串s和t,每次能让一个字符与其相差k或k+1个距离的字符进行交换,问能否让字符串s通过若干次该操作使其变为字符串t
问题分析
方法1分析性质
1.分析操作对元素位置的影响
使用一次该操作,可以让当前元素与另一个元素交换位置,再用一次则可以将该元素放到与之相邻的位置,再一次则可以让相邻两个元素完成交换,且不影响其余元素。
2.分析可以使用操作的元素可以与相邻元素交换位置的作用
对于可以使用该操作的元素,其可以与相邻元素交换位置,则所有可以使用该操作的元素之间可以通过连续使用该操作,从而可以任意交换顺序,则对于这些位置的元素只需检查两个字符串字符是否对应都有即可。而不能使用该操作的元素则需要单独比较是否对应相等
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N =210, Mod = 998244353, P = 2048;
void solve() {
int n,k;
cin >>n >>k;
string s,t;
cin >>s >>t;
vector<int> cnt(26,0);
bool ok=true;
for(int i=0;i<n;i++){
if(i>=k||i+k<n){
cnt[s[i]-'a']++,cnt[t[i]-'a']--;
}else {
ok&=(s[i]==t[i]);
}
}
if(ok&&count(cnt.begin(),cnt.end(),0)==26) puts("YES");
else puts("NO");
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
方法2通过DFS得到相互可以交换位置的字符集合
每个位置一次操作可以到达的点为一个可以相互交换的集合,可以先通过DFS得到每个点所属集合。然后将两个字符串中对应位置的字符存入所在集合里,排序后形成形式同一的字符串进行比较。
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N =2e5+10, Mod = 998244353, P = 2048;
int n,k;
int a[N];
vector<char> g[N];
int idx;
void dfs(int u,int idx){
if(u<0||u>=n||a[u]) return ;
a[u]=idx;///记录u点所在集合编号
dfs(u+k,idx),dfs(u+k+1,idx),dfs(u-k,idx),dfs(u-k-1,idx);
}
string make(string str){
for(int i=1;i<=idx;i++){
g[i].clear();
}
for(int i=0;i<n;i++){
///将各点字符存入所属集合内
g[a[i]].push_back(str[i]);
}
for(int i=1;i<=idx;i++){
///将各个集合内的字符排序
sort(g[i].begin(),g[i].end());
}
string ans;
///将各个集合内的元素弄成形式统一的字符串用于比较
for(int i=0;i<n;i++){
ans+=g[a[i]].back();
g[a[i]].pop_back();
}
return ans;
}
void solve() {
cin >>n >>k;
string s,t;
cin >>s >>t;
idx=0;
memset(a,0,sizeof(int)*(n+1));
for(int i=0;i<n;i++){
if(!a[i]){
dfs(i,++idx);
}
}
if(make(s)==make(t)) puts("YES");
else puts("NO");
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
方法3通过并查集连通可交换位置
先通过并查集将每个位置可到达的位置连通,然后将连通的字符进行记录,最后比较两个字符串每个连通集内的字符。
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N =2e5+10, Mod = 998244353, P = 2048;
int n,k;
int a[N];
int cnt[N][26];
int p[N];
int find(int x){
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void solve() {
cin >>n >>k;
string s,t;
cin >>s >>t;
for(int i=0;i<n;i++) p[i]=i;
for(int i=0;i<n;i++) memset(cnt[i],0,sizeof(int)*(26));
///将可以相互到达的位置连通
for(int i=0;i<n;i++){
if(i+k<n){
int fa=find(i),fb=find(i+k);
if(fa!=fb){
p[fa]=fb;
}
}
if(i+k+1<n){
int fa=find(i),fb=find(i+k+1);
if(fa!=fb){
p[fa]=fb;
}
}
}
for(int i=0;i<n;i++){
///将该位置字符记录在对应连通块内
cnt[find(i)][s[i]-'a']++;
cnt[find(i)][t[i]-'a']--;
}
for(int i=0;i<n;i++){
if(find(i)==i){
///检查该连通块内字符是否相等
for(int j=0;j<26;j++){
if(cnt[i][j]!=0){
puts("NO");
return ;
}
}
}
}
puts("YES");
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}