传送门UVa 10716 - Evil Straw Warts Live
题意是给出一串字符串,判断是否能成为回文串,如果能的话输出最小移动次数。
贪心的思想。对于每个位置,搜索一遍字符串,找出移动到这个位置的最小步数,移动这个字母,再接着搜索下一个位置。
看到一些解题报告,直接搜索这个位置和对称的那个位置上的字母,看哪个距离更短。感觉这样不太靠谱。
比如abbcddceea,如果只判断b和e,都是6步,但是事实上最短的是移动c,4步。
所以还是应该扫描一遍字符串的。
参考了洪婉萍的解题报告。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 110;
char str[MAXN];
int vis[MAXN], check[30];
int Solve()
{
int i, j, len, first, last, left, right, lastOccur, minDis, cnt = 0;
//first, last是记录最终要移动的点的位置,left, right记录当前填填补的位置
len = strlen(str);
left = 0, right = len - 1;
while (left < right)
{
memset(vis, 0, sizeof(vis));
minDis = MAXN;
for (i = left; i < right; i++)
{
if (!vis[i])
{
lastOccur = i; //防止当扫描到的是唯一的字母时,lastOccur没被初始化,导致出错
vis[i] = 1;
for (j = right; j >= i + 1; j--)
if (str[i] == str[j])
{
vis[j] = 1;
lastOccur = j;
break;
}
if (i - left + right - lastOccur < minDis)
{
minDis = i - left + right - lastOccur;
first = i, last = lastOccur;
}
}
}
for (i = first; i > left; i--)
{
swap(str[i], str[i - 1]);
cnt++;
}
for (i = last; i < right; i++)
{
swap(str[i], str[i + 1]);
cnt++;
}
left++, right--;
}
return cnt;
}
int main()
{
//freopen("input.txt", "r", stdin);
int T, i, j, cnt;
scanf("%d%*c", &T);
while (T--)
{
memset(check, 0, sizeof(check));
cnt = 0;
gets(str);
for (i = 0; i < strlen(str); i++)
check[str[i] - 'a']++;
for (i = 0; i < 26; i++)
if (check[i] % 2)
cnt++;
if (cnt > 1)
printf("Impossible\n");
else
printf("%d\n", Solve());
}
return 0;
}