问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3
算法分析 :
看到求最值,想到贪心法,这题我觉得更合适叫动态规划
1 如何判断是否是回文
利用字符数组的长度定中间值(奇数有一个 偶数两个 不影响判断 不然分情况太麻烦了 所以我选择不管几个都可以的方法)
把第一个a【0】与最后一个a【n-1】变成相同的字母
从a【n-1】开始往前遍历,寻找和a【0】相同的字母 a【i】
移动的次数为 n-1-i
把第a【a】与a【b】变成相同的字母
从a【b】开始往前遍历,寻找和a【a】相同的字母 找到的为a【i】
移动的次数为 b-i
a+1 b-1 重复上述步骤
直到a=b 时 此时已经为回文结构
2 判断此时的a[a]是否为中间字符,即该字符未匹配到其他字符
若 a【a】不止一个中间字符 直接计算最后中间字符移至中间需要交换的次数
跳过中间字符,判断后面是否满足回文
package bluebrige;
import java.util.Scanner;
public class test
{
private static int count=0;
private static boolean haveOnlyOneMiddle=true;
public static void main(String args[]) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
String str=sc.next();
char[] charArr = str.toCharArray();
if(palindrome(charArr,0,n-1))
System.out.println(count);
else
System.out.println("Impossible");
}
private static boolean palindrome(char[] charArr, int a, int b) {
// TODO 自动生成的方法存根
if(a>=b) {//此时代表交换后满足了回文数
return true;
}
//由后往前遍历 匹配首字符
for(int i=b;i>a;i--) {
if(charArr[a]==charArr[i]) {
exchangeTo(charArr,i,b);//匹配到了依次进行交换
count+=b-i;
return palindrome(charArr,a+1,b-1);//再重复判断接下来是否满足回文
}
}
//判断此时的charArr[a]是否为中间字符,即该字符未匹配到其他字符
if(haveOnlyOneMiddle) {
haveOnlyOneMiddle=false;//有中间字符但仅能出现一个中间字符
count+=charArr.length/2-a;//直接计算最后中间字符移至中间需要交换的次数
return palindrome(charArr,a+1,b);//跳过中间字符,判断后面是否满足回文
}
return false;
}
//字符交换
private static void exchangeTo(char[] charArr, int i, int b) {
// TODO 自动生成的方法存根
char temp;
for(int k=i;k<b;k++) {
temp=charArr[k];
charArr[k]=charArr[k+1];
charArr[k+1]=temp;
}
}
}