基础练习 完美的代价
作者:互联网
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如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