其他分享
首页 > 其他分享> > 基础练习 完美的代价

基础练习 完美的代价

作者:互联网

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

分析:
本题要使用贪心算法。

(1)形如“mama”,每个字符出现的次数都是偶数次,可以得到完美的回文串“maam”或者“amma”。
(2)形如“mamad”,只有1个字符出现次数是奇数次,其余都是偶数次,可以得到完美的回文串“madam”或者“amdma”。
(3)形如“mamade”,有2个字符出现次数是奇数次,其余的都是偶数次,得不到完美的回文串。
以此类推,可以发现,字符串中出现奇数次的字符大于等于2的时候,得不到完美的回文串,输出“Impossible”

具体算法:
先定义i,k,两个变量,i从字符串最左边开始往右遍历,k从最右边开始往左遍历。如图:

i不变,k自减往左遍历,如果a[i]和a[k]不一样,则让k继续往左遍历,直到和a[i]相等,如下:

定义一个变量j,让他指向最后一个未经过交换对称的字符,最大值为n-1。
将a[k]移动到与a[i]对称的位置,可以通过a[k]=a[k+1]实现,条件为k<j,最后a[j]=a[i]即可,在此过程中,定义一个记录移动次数的变量sum=0,每次移动字符时加1。
由于移动后,变量i和j所指向的字符已形成对称,后面的移动不需改变其位置,可以让j=j-1,即j指向前一个,i++,i指向后一个,接下来只需要考虑红框内的字符,如下:

每一轮都让k==j,保证了k每次都从未经对称的末尾字符开始往左遍历,接下来k遇到m,然后移动,和上面的方法一样。

但是,如果一直找不到相等的字符,怎么办?

像这种情况,如果一直找不到相同的,最后会遇到i等于k的情况,当i等于k的时候,我们可以知道a[i]在数组内独一无二,我们是不是可以把它移动到中间,共移动(数组长度/2-i)次,此时并不移动它(若此时移动将增加代价,应将它放在最后移动),把剩下的看成一个整体,如下:

i++,i指向下一个字符,考虑矩形内的字符,用上面的方法移动。
如果出现两次i等于j的情况,则代表数组内存在两个独一无二的字符,不可能存在完美的回文串,结束程序,打印Impossible.
当i<j不满足时,代表对称交换已经执行完毕,可以输出sum的值。

代码:

#include <stdio.h>
#include <stdlib.h>
int main() {
	int n,sum=0;
	int i,j,k,m,flag=0;
	char a[8001],c;
	scanf("%d",&n);
	getchar();
	gets(a);
	j=n-1;
	for(i=0;i<j;i++)
	{
		for(k=j;k>=i;k--)
		{
			if(i==k)
			{
				if(n%2==0)
				{
					printf("Impossible");
					return 0;
				}
				else
				{
					if(flag)
					{
						printf("Impossible");
						return 0;
					}
				}
				sum+=n/2-i;
				flag=1;
				break;
			}
			if(a[i]==a[k])
			{
				for(m=k;m<j;m++)
				{
					a[m]=a[m+1];
					sum++;
				}
				a[j]=a[i];
				j--;
				break;
			}
		}
	}
	printf("%d",sum);
	return 0;
}
是小葱哦 发布了33 篇原创文章 · 获赞 19 · 访问量 1467 私信 关注

标签:字符,遍历,完美,练习,Impossible,移动,代价,回文
来源: https://blog.csdn.net/weixin_43804406/article/details/104116126