有一段时间没有用c++写过程序了,偶尔心血来潮刷两道水题,没想到居然被一道题卡住了。。。。很难受emmmm
写出来给大家分享下,觉得这道题挺精妙的,有值得学习的地方(大神可忽略)
题目如下:
问题描述 回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!) 输入格式 第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
第二行是一个字符串,长度为N.只包含小写字母 输出格式 如果可能,输出最少的交换次数。
否则输出Impossible 样例输入 5
mamad 样例输出 3
看上去并不难,一开始我的思路也正确,先假设前面的不动,从后往前遍历字符串,遇到相同的字符则移动到对应位置,并记录下移动的次数。
可是我却被中间那个字符卡住了,字符串长度是偶数很好办,奇数则会多出一个字符不需匹配,放在中间即可,原本我的思路是先给一次机会,遇到匹配不成功的则移到中间(奇数情况下),可是接下来继续匹配的时候又出了问题,由于操作字符串,那个单独的字符产生了偏移,不在中间了。
于是我又把它操作了回去,结果一来一回多走了两次。。。
好在最终我想出了办法,就是先忽略这个特殊字符,继续匹配直到完成,最后再来处理它,代码如下:
#include<cstdio>
#include<string>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cmath>
using namespace std;
int main()
{
string str;
int n,j,cnt=0,flag=0,index; //cnt为移动次数,flag记录唯一一次不匹配的机会
cin>>n>>str;
int last=n-1; //last很关键,记录了未完成匹配那部分字符串的末位
for(int i=0;i<last;i++)
{
for(j=last;j>i;j–)
{
if(str[i]==str[j])
{
for(int u=j;u<last;u++)
str[u]=str[u+1];
str[last]=str[i];
cnt+=last-j;
–last;
break;
}
}
if(j==i&&(n%2==0||flag==1)) //如果匹配不到且(字符串长度为偶数或者机会用完),那么再见
{
cout<<“Impossible”<<endl;
return 0;
}
else if(j==i) //使用不匹配的那次机会,并记录特殊字符的位置
{
flag=1;
index=i;
}
}
if(flag==0)
cout<<cnt<<endl;
else
cout<<cnt+n/2-index<<endl; //将不匹配的特殊字符移到中间(因为求次数所以直接修改cnt即可)
return 0;
}
这样问题就解决了,先忽略那个特殊字符不进行处理的方法的确挺巧妙,值得一记。