一、题目描述
回⽂串,是⼀种特殊的字符串,它从左往右读和从右往左读是⼀样的。⼩⻰⻰认为回⽂串才是完美
的。现在给你⼀个串,它不⼀定是回⽂的,请你计算最少的交换次数使得该串变成⼀个完美的回⽂
串。
交换的定义是:交换两个相邻的字符
例如mamad
第⼀次交换 ad : mamda
第⼆次交换 md : madma
第三次交换 ma : madam (回⽂!完美!)
输⼊格式
第⼀⾏是⼀个整数N,表示接下来的字符串的⻓度(N <= 8000)
第⼆⾏是⼀个字符串,⻓度为N.只包含⼩写字⺟
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输⼊
5
mamad
样例输出
3
二、题解
方法一:贪心
思路
- 先判断字符串是否能够组成回文串,统计每个字符出现的次数。
- 如果出现次数为奇数的字符的个数 >= 2 是不能组成回文串的。比如,“abcd”.
- 查找是否存在不同位置的相同字母。
- 从哪开始反向找?应该从当前位置的镜像位置的开始反向查找,即 N-cur-1。
- 从后往前递归地查找与当前位置 cur 字符相同的另一个字符的最后出现的位置 findLast()。
- 如果找到,将其与 cur 的镜像位置交换。
- 找不到怎么办?cur 位置的字符将是出现次数为奇数字符。然后将 cur 与 cur+1 位置字符进行交换,然后再查一次。
- 比如在 dmamma 中查找 d,找不到则将 d 与 m 交换,mdamma;cur 位置还是不变。
* 核心逻辑:从外往里地将与当前位置 cur 相同的字符移动到 N-cur-1 位置(即镜像位置),这样可保证移动的次数最小。
public class Main {
char[] s;
int N;
int res;
public static void main() {
Scanner sc = new Scanner(System.in);
//1. 统计出现次数为奇数次的字符个数
N = sc.nextInt();
s = sc.next().toCharArray();
int[] map = new int[26];
for (char c : s) map[c - 'a']++;
int cnt = 0;
for (int n : map) {
if (n % 2 == 1)
cnt++;
}
if (cnt >= 2) {
System.out.println("Impossible");
return;
}
//主干
for (int l = 0; l < N/2; l++) {
int lastI = find(i);
swap(lastI, r);x
}
return;
}
//交换
private static void swap (int l, int r) {
if (l == r || s[l] == s[r])
return;
int temp = s[l+1];
s[l+1] = s[r];
s[r] = temp;
res++;
swap(l+1, r);
}
//找到与当前字符相同的最后的出现位置
private int find(int cur) {
for (int i = N-cur-1; i > cur; i--) {
if (s[i] = s[cur])
return i;
}
swap(cur, cur+1); //如果找不到
return find(cur);
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,