文章目录
- [ 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;
}