一:题目
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
样例输入
5
mamad
样例输出
3

二:分析
这道题目首先拿到手,由于我比较菜,没有什么思路,参考了一下网上别人的思路,普遍都是用两个for循环实现前后同时遍历,但是我比较疑惑的是,他们的字符串是怎么变化的,貌似并没有随着每次的“移动次数”增加而动态变化。
https://blog.csdn.net/liuchuo/article/details/51990430)
https://blog.csdn.net/xiao_bai_9527/article/details/78461178)
我的方法:
我的核心函数fun中是把奇数长度和偶数长度字符串情况分开来。这样做是为了区别遇到“无法变为回文字符串的字符串”时的操作。其他部分操作是类似的:
首先需要用两次for循环前后遍历字符串,由于我的方法是让字符串每次变化后实时更新,所以其实第一层遍历只需要遍历到字符串的中间即可。

for(int i=0;i<n/2;i++)
			{
				for(int j=n-1-i;j>=i;j--)
				{
					if(i==j)  //只要一次匹配不到,就不可能产生回文字符串
					{	
						//********这部分后面再讨论
					}
					else if(s.charAt(i)==s.charAt(j))
					{
						numOfMove+=n-1-i-j; //从j移动到n-1-i位置
						s=swap(s,i,j,n);
						break;
					}
				}
			}

如下图所示:i向后进时,方框外面的区域已经**“局部回文”了,故i只需要遍历到字符串中间就行了。
蓝桥基础练习之完美的代价★(动态更新)
更新字符串的函数swap代码如下:
由于循环参数的设置,这个函数的
i总会是小于j**,也就是只可能是右移字符的操作。

```
public static StringBuilder swap(StringBuilder sb,int i,int j,int n)  //字符顺序右移函数
	{
		StringBuilder stemp = new StringBuilder("");	
		stemp.append(sb.substring(0, j));
		stemp.append(sb.substring(j+1, n-i));
		stemp.append(sb.charAt(j));
		stemp.append(sb.substring(n-i, n));
		return stemp;		
	}
``

这里有两点注意:
①s.substring(int a,int b),返回的是从字符串中索引位置a到b-1的子串。
②substring函数中若a=b,返回的字符串是空字符串。

蓝桥基础练习之完美的代价★(动态更新)

下面就只剩下一个问题了,当在遍历过程中出现“孤儿字符”该怎么办,上面我说到了,奇偶长度字符串我是分开处理的。对于偶长度字符串,只要出现一个孤儿字符,它就不可能变为回文字符串。而奇长度字符至多允许出现一个孤儿字符。

对于奇长度字符串,若出现了一个孤儿字符,不能直接用之前的swap函数,两者定义不相符,我的做法是:
由于循环参数的设置,孤儿字符的位置肯定在中间字符的左边,那么直接把孤儿字符挪动到中间位置不就可以了,这样移动次数就是两者位置相减。
这样做是由两点要求的:①字符串实时更新,不能用swap函数。②操作完成后先把i-1再跳出内循环(更新字符串后,由于孤儿字符前移了,前面的字符就会往后缩)。

for(int i=0;i<(n-1)/2;i++)
			{
				for(int j=n-1-i;j>=i;j--)
				{
					if(i==j)
					{
						flag++;
						if(flag>1)  //孤儿字符超过1个,不可能产生回文字符串
						{
							System.out.println("Impossible");
							return;
						}
						numOfMove+=(n-1)/2-i;
						s=new StringBuilder("").append(s.substring(0, i)).append(s.substring(i+1, (n+1)/2))
								.append(s.charAt(i)).append(s.substring((n+1)/2, n));
						i--;  //由于把孤儿字符移动到字符串中间,当前i指向字符到中间之前的字符自然就往前挪动了一格
						break;
					}
					else if(s.charAt(i)==s.charAt(j))
					{
						numOfMove+=n-1-i-j; //从j移动到n-1-i位置
						s=swap(s,i,j,n);
						break;
					}
				}			

挪动部分的原理可以看看下图:
蓝桥基础练习之完美的代价★(动态更新)

三:完整代码以及部分运行结果

import java.util.Scanner;
public class Main {
	public static void main(String[] args){
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		String s = in.next();
		StringBuilder sb = new StringBuilder(s);
		fun(sb);
	}	
	public static void fun(StringBuilder s)
	{
		int numOfMove = 0;  //移动次数
		int n=s.length();
		if(n%2==0)  //字符串为偶长度
		{
			for(int i=0;i<n/2;i++)
			{
				for(int j=n-1-i;j>=i;j--)
				{
					if(i==j)  //只要一次匹配不到,就不可能产生回文字符串
					{
						System.out.println("Impossible");
						return;
					}
					else if(s.charAt(i)==s.charAt(j))
					{
						numOfMove+=n-1-i-j; //从j移动到n-1-i位置
						s=swap(s,i,j,n);
						break;
					}
				}
			}
		}
		else  //字符串为奇长度
		{
			int flag=0; //孤儿字符个数
			for(int i=0;i<(n-1)/2;i++)
			{
				for(int j=n-1-i;j>=i;j--)
				{
					if(i==j)
					{
						flag++;
						if(flag>1)  //孤儿字符超过1个,不可能产生回文字符串
						{
							System.out.println("Impossible");
							return;
						}
						numOfMove+=(n-1)/2-i;
						s=new StringBuilder("").append(s.substring(0, i)).append(s.substring(i+1, (n+1)/2))
								.append(s.charAt(i)).append(s.substring((n+1)/2, n));
						i--;  //由于把孤儿字符移动到字符串中间,当前i指向字符到中间之前的字符自然就往前挪动了一格
						break;
					}
					else if(s.charAt(i)==s.charAt(j))
					{
						numOfMove+=n-1-i-j; //从j移动到n-1-i位置
						s=swap(s,i,j,n);
						break;
					}
				}			
			}
		}
		System.out.println(numOfMove+"\n"+s);
	}
	public static StringBuilder swap(StringBuilder sb,int i,int j,int n)  //字符顺序右移函数
	{
		StringBuilder stemp = new StringBuilder("");	
		stemp.append(sb.substring(0, j));
		stemp.append(sb.substring(j+1, n-i));
		stemp.append(sb.charAt(j));
		stemp.append(sb.substring(n-i, n));
		return stemp;		
	}
}


题目其实没要求输出最后的回文字符串,最后去掉就可以了。

蓝桥基础练习之完美的代价★(动态更新)

蓝桥基础练习之完美的代价★(动态更新)

蓝桥基础练习之完美的代价★(动态更新)

蓝桥基础练习之完美的代价★(动态更新)

相关文章:

  • 2022-12-23
  • 2021-09-17
  • 2021-04-21
  • 2021-05-01
  • 2021-08-18
  • 2021-11-21
  • 2021-06-28
猜你喜欢
  • 2021-08-10
  • 2022-12-23
  • 2022-12-23
  • 2021-12-26
  • 2021-08-01
  • 2022-12-23
相关资源
相似解决方案