Reputation: 99
Given a string, I need to find the longest palindrome that can be constructed by removing or shuffling characters from the string. If more than one palindrome exists of same length then I need to make ensure that lexicographically smallest one is been given as output. Example : "adskassda" Output expected is : "adsasda"
I am able to find largest palindrome, but how to ensure in case of multiple of same maximum length lexicographically smallest one is been given as output ?
Any palindromic string can be divided into three parts – beg, mid and end. For palindromic string of odd length say 2n + 1, ‘beg’ consists of first n characters of the string, ‘mid’ will consist of only 1 character i.e. (n + 1)th character and ‘end’ will consists of last n characters of the palindromic string. For palindromic string of even length 2n, ‘mid’ will always be empty. It should be noted that ‘end’ will be reverse of ‘beg’ in order for string to be palindrome. I have used same logic for this too.
#include <bits/stdc++.h>
using namespace std;
string longestPalindrome(string str){
map<char,int> frequencyChar;
for(int i=0;i<str.length();i++){
frequencyChar[str[i]]++;
}
char middle_character;
string leftStr;
for(auto it: frequencyChar){
char currentChar=it.first;
int frequencyCurrentChr = it.second;
if(frequencyCurrentChr%2!=0){
middle_character=currentChar;
}
leftStr.append(frequencyCurrentChr/2,currentChar);
}
string rightStr(leftStr.rbegin(),leftStr.rend());
return leftStr + middle_character + rightStr;
}
int main() {
string str = "adskassda";
cout<<longestPalindrome(str);
}
I am getting "adsssda" but expected is "adsasda"
Upvotes: 4
Views: 7093
Reputation: 1707
I added a small change in the code - Sort lexicographically as soon as we get the left part. This may be required in Java. When I wrote the above code in java, I got "asdadsa" instead of "adsasda"
Here is the code in Java:
import java.util.*;
public class Solution {
public static String longPalindrome(String a) {
Map<Character, Integer> map = new HashMap<>();
for(int i=0; i<a.length(); i++) {
map.put(a.charAt(i), map.getOrDefault(a.charAt(i), 0) + 1);
}
char mid = 0;
boolean mid_chosen = false;
StringBuilder left = new StringBuilder();
for(Map.Entry<Character, Integer> entry : map.entrySet()) {
if(!mid_chosen && entry.getValue() % 2 != 0) { //odd
mid_chosen = true;
mid = entry.getKey();
}
//Adding elements to left
for(int k=0; k<entry.getValue()/2; k++) {
left.append(entry.getKey());
}
}
//New Step added to sort it lexicographically
char[] leftChArr = left.toString().toCharArray();
Arrays.sort(leftChArr);
StringBuilder leftC = new StringBuilder(new String(leftChArr));
StringBuilder right = new StringBuilder();
//adding reverse elements to left
for(int j=leftC.length()-1; j>=0; j--) {
right.append(leftC.charAt(j));
}
if(mid_chosen == true) {
leftC.append(mid).append(right);
} else {
leftC.append(right);
}
return leftC.toString();
}
public static void main(String[] args) {
String str = "adskassda";
System.out.println(longPalindrome(str));
}
}
There may be some refactoring required, example using existing lib in Java to add repeat characters in StringBuilder. But the above code provides the solution.
Upvotes: 0
Reputation: 1810
This seemed to work for me, although my testing was far from extensive:
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
string in("adskassda");
map<char, int> chars;
string out;
for (auto c : in)
{
++chars[c];
}
string middle;
for (auto e : chars)
{
if (e.second >= 2)
{
out.append(e.second/2, e.first);
e.second = e.second%2;
}
if (e.second && middle.empty())
middle = e.first;
}
string tail(out);
reverse(tail.begin(), tail.end());
out = out + middle + tail;
cout << in << endl;
cout << out << endl;
}
Upvotes: 0
Reputation: 1798
You only have a simple error. When you want to chose the middle character, the first time you see one with and odd frequency, you should choose it and never update it again, because that'll be the one with the lowest lexicographical order. That's why I've added the boolean variable mid_char_chosen
and once it's set to true it will not be updated again. There's another corner case you haven't considered: if all frequencies are even then there'll be no midle character and the result will have even number of characters. So the output should ommit middle character. With these minor modifications, I think the code runs:
#include <bits/stdc++.h>
using namespace std;
string longestPalindrome(string str){
map<char,int> frequencyChar;
for(int i=0;i<str.length();i++){
frequencyChar[str[i]]++;
}
char middle_character;
string leftStr;
bool mid_char_chosen = false;
for(auto it: frequencyChar){
char currentChar=it.first;
int frequencyCurrentChr = it.second;
if(!mid_char_chosen and frequencyCurrentChr%2!=0){
middle_character=currentChar;
mid_char_chosen = true;
}
leftStr.append(1*(frequencyCurrentChr/2),currentChar);
}
string rightStr(leftStr.rbegin(),leftStr.rend());
if (mid_char_chosen)
return leftStr + middle_character + rightStr;
else
return leftStr + rightStr;
}
int main() {
string str = "adskassda";
cout<<longestPalindrome(str) << endl;
}
Upvotes: 0