#P2879. 第2题-字符交换
-
1000ms
Tried: 111
Accepted: 27
Difficulty: 3
所属公司 :
美团
时间 :2025年4月19日-技术岗
-
算法标签>思维
第2题-字符交换
题解
题面描述
给定一个长度为 n 的字符串 s,允许恰好进行一次交换操作:选定两个不同索引 x=y,将 sx 和 sy 互换。判断能否通过这一次交换使得最终字符串满足
s0≤s1≤s2≤⋯≤sn−1.若可以,输出 YES,否则输出 NO。
输入包含 t 个测试用例,满足
- 1≤t≤1000,
- 每个字符串长度 1≤n≤105,
- 所有测试用例中 ∑n≤105。
思路
- 目标串:将原串 s 进行排序,记为目标串 t。
- 统计差异:遍历下标 0≤i<n,统计 si=ti 的位置个数,记为 cnt。
- 判断条件:
- 若 cnt=2,则正好有两处字符错位,一次交换即可完成排序,输出
YES。 - 若 cnt=0,表示原串已是非降序,但仍须恰好进行一次交换。此时只有当存在相同字符时,才可交换两相同字符而不破坏顺序,输出
YES;否则交换必破坏顺序,输出NO。 - 其它情况下(cnt=1 或 cnt>2),一次交换无法完成排序,输出
NO。
- 若 cnt=2,则正好有两处字符错位,一次交换即可完成排序,输出
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
cin >> s;
// 将字符串 s 排序得到目标串 t
string t = s;
sort(t.begin(), t.end());
// 统计 s 和 t 在对应位置上不同的次数
int cnt = 0;
for (int i = 0; i < n; ++i) {
if (s[i] != t[i]) {
cnt++;
}
}
if (cnt == 2) {
// 恰好两处不同,通过一次交换即可排序
cout << "YES\n";
} else if (cnt == 0) {
// 已有序,但必须进行一次交换
// 只有存在重复字符时,交换相同字符才不会破坏顺序
vector<int> freq(26, 0);
bool dup = false;
for (char c : s) {
if (++freq[c - 'a'] > 1) {
dup = true;
break;
}
}
cout << (dup ? "YES\n" : "NO\n");
} else {
// 其它情况无法通过一次交换排序
cout << "NO\n";
}
}
return 0;
}
Python
import sys
from collections import Counter
def solve():
# 读取测试用例数量
t = int(sys.stdin.readline())
for _ in range(t):
# 读取字符串长度和内容
n = int(sys.stdin.readline())
s = sys.stdin.readline().strip()
# 排序得到目标非降序字符串
t_str = ''.join(sorted(s))
# 记录不同位置的索引
diff = [i for i in range(n) if s[i] != t_str[i]]
cnt = len(diff)
if cnt == 2:
# 恰好两处不同,可交换排序
print("YES")
elif cnt == 0:
# 已有序,但必须进行一次交换
# 只有存在重复字符时,交换相同字符才无害
if any(v > 1 for v in Counter(s).values()):
print("YES")
else:
print("NO")
else:
# 其它情况无法完成
print("NO")
if __name__ == '__main__':
solve()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 读取测试用例数量
int t = Integer.parseInt(br.readLine());
while (t-- > 0) {
// 读取字符串长度和内容
int n = Integer.parseInt(br.readLine());
String s = br.readLine();
// 将字符串转换为字符数组并排序得到目标
char[] a = s.toCharArray();
char[] b = s.toCharArray();
Arrays.sort(b);
// 统计不同位置数
int cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
cnt++;
}
}
if (cnt == 2) {
// 恰好两处不同,可交换排序
System.out.println("YES");
} else if (cnt == 0) {
// 已有序,但必须交换一次
// 只有存在重复字符时才可交换相同字符不破坏顺序
int[] freq = new int[26];
boolean dup = false;
for (char c : a) {
if (++freq[c - 'a'] > 1) {
dup = true;
break;
}
}
System.out.println(dup ? "YES" : "NO");
} else {
// 其它情况无法一次交换排序
System.out.println("NO");
}
}
}
}
题目内容
小美现在有一个字符串 s,她现在进行恰好一次操作。
- 交换两个索引的字母,即选定 x,y(x=y),交换 sx 和 sy。
她想知道能不能使得 s 满足 s0≤s1≤s2≤⋅⋅⋅≤sn−1。
你能帮帮她吗?
输入描述
第一行一个整数 t,表示有 t(1≤t≤1000) 组数据。
每组数据第一行一个整数 n(1≤n≤100000),表示字符串的长度。
每组数据第二行一个字符串 s。
s 仅包含小写字母且单个测试文件满足 ∑n≤105。
输出描述
对于每组数据,如果可以使得 s 满足 s0≤s1≤s2≤⋅⋅⋅≤sn−1,则输出 YES,否则输出 NO。
示例1
输入
2
3
acb
3
bca
输出
YES
NO