一.题目
题目链接
http://lx.lanqiao.cn/problem.page?gpid=T60
问题描述
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)
输入格式
第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母
第二行是一个字符串,长度为N.只包含小写字母
输出格式
如果可能,输出最少的交换次数。
否则输出Impossible
否则输出Impossible
样例输入
5
mamad
mamad
样例输出
3
二.解决
思路:交换的定义是交换两个相邻的字符!最后交换成功的结果是两边字母对称。
问题:什么字符串一定不可能成为回文串?
字符串有大于等于2种的字母个数为奇数 也即:回文串只能最多有一种字母其个数为奇数,其余都应为偶数
以下是建立在该字符串一定为回文串的处理方法:
从前向后遍历字符串,对每一个字符找到与该字符相同字符的位置,因为要使得移动次数最少,所以可以从后往前遍历字符串,找到第一个与它相等的字符,并且移动到对称的位置,统计移动的次数。
从左边往右边走 i = 0 to “对称位置” i初始位置为0,对称位置为最右边,也即n-1 i++
从右边往左边走 j = “对称位置” to i+1 找到第一个与a[i]值(字母)相同的j 然后去移动 使得i的对称位置的字母为a[j] 总移动次数+=这里移动的次数 “对称位置”也往左移动一位(“对称位置”--)
最后,我们要考虑一个问题,也即如果遇到个数只有一个的字母时我们应该要做的事情,我们要确保它在中间位置,个数只有一个的字母判断条件:当遍历完右边后都没有字母与它相等,这时j==i(也即遍历到了自身),我们需要将这个字母移动到中间,只需将总移动次数+=(N/2-i),不需要真正将它移动,因为在后面的寻找对称字母过程中,可能会将“这中间字母”又移动到其他地方,白做功夫,所以我们暂时先不移动它,等到所有的寻找过程完成之后,再对它进行移动(当然我们其实并不需要真的去移动它,因为我们已经知道移动次数:中间位置N/2-当前位置i)。
#include<bits/stdc++.h> using namespace std; #define maxn 8005 int mp[30]; int main(){ int N,sum=0,k,i,j,flag=0,flag1=0; string s; cin>>N; cin>>s; memset(mp,0,sizeof(mp)); //判断字符串能否成为回文串 for(int i=0;i<N;i++){ mp[s[i]-'a']++; } for(int i=0;i<24;i++){ if(mp[i]%2==1) flag1++; if(flag1>1) break; } if(flag1>1) printf("Impossible "); else{ int temp=N-1; for(i=0;i<temp;i++){//从前向后找 for(j=temp;j>i;j--){//从后向前找 if(s[i]==s[j]){//遇到相等的字母则需要交换到合适的地方 //从j位置移动到与i对称的位置(即temp) for(k=j;k<temp;k++){ s[k]=s[k+1]; } s[temp]=s[i];//也即之前的s[j],但s[j]已经被其他字母占了,所以用与它值相等的s[i]表示 sum+=temp-j;//这次移动的次数 temp--;//缩小边界 break; } } if(j==i){//也即之前的“从后向前找”找不到相等的字母,只有自身,则将它移动到中间 sum+=(N/2 - i); } } cout<<sum<<endl; } }