Codeforces 600C Make Palindrome 【贪心 找字典序最小回文串】

一、题目概述

C. Make Palindrome
time limit per test
 2 seconds
memory limit per test
 256 megabytes
input
 standard input
output
 standard output

A string is called palindrome if it reads the same from left to right and from right to left. For example "kazak", "oo", "r" and "mikhailrubinchikkihcniburliahkim" are palindroms, but strings "abb" and "ij" are not.

You are given string s consisting of lowercase Latin letters. At once you can choose any position in the string and change letter in that position to any other lowercase letter. So after each changing the length of the string doesn't change. At first you can change some letters in s. Then you can permute the order of letters as you want. Permutation doesn't count as changes.

You should obtain palindrome with the minimal number of changes. If there are several ways to do that you should get the lexicographically (alphabetically) smallest palindrome. So firstly you should minimize the number of changes and then minimize the palindrome lexicographically.

Input

The only line contains string s (1 ≤ |s| ≤ 2·105) consisting of only lowercase Latin letters.

Output

Print the lexicographically smallest palindrome that can be obtained with the minimal number of changes.

Sample test(s)
input
aabc
output
abba
input
aabcd
output
abcba

二、题目释义

给定一个只包含小写字母的字符串,你可以修改任意位置的字符(变换为a-z中任一个),然后重新排列字符串。现在要求用最少次数的修改得到一个回文串,若有多种方案,输出字典序最小的方案。

三、思路分析

题目本身有很多特殊条件可以利用,比如回文,偶数长的字符串所有字母出现的次数都为偶数,奇数长的字符串,必有一个字母出现次数为1次,那么对于偶数长度的字符串我们就要让所有那些多了一个(必然是只多一个),我们就标记他们,并且让字典序最大的变成最小的,次大的的变成次小的,而且容易知道,它的那些多了一个的字母也必然是偶数个;对于奇数长的字符串,多了一个的字母个数必然为奇数个,我们把中间的那个字母放在整个回文串的中间。

四、AC代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cstring>
using namespace std;
const int N = 2e5+5;
char s[N];
int num[26]={0};
// int mark[26]={0};
char trans[26]={0};

int main()
{
    scanf("%s",s);
    int len = strlen(s);
    int unuse = 0; char mid = '0';
    for(int i=0; i<len; i++)
        num[s[i]-'a']++;
    for(int i=0; i<26; i++)
    {
        if(num[i]%2)
            trans[++unuse] = 'a'+i;
    }
    /**cout << unuse << endl;*/
    for(int i=1; i<=unuse/2; i++)
    {
        num[trans[i]-'a']++;
        num[trans[unuse-i+1]-'a']--;
    }
    if(len%2) mid = trans[unuse/2+1];
    int F=0, B=len-1; // 我们最后是通过记录改变完字母后的每个字母的出现次数来按照字典序重排字符串的
    for(int i=0; i<26; i++) 
    {
        /**cout << num[i] << " ";*/
        for(int j=F; j<F+num[i]/2; j++)
            s[j] = 'a' + i;
        F += num[i] / 2;
        for(int j=B; j>B-num[i]/2; j--)
            s[j] = 'a' + i;
        B -= num[i] / 2;
    }
    if(len%2) s[len/2] = mid;
    cout << s << endl;
    return 0;
}