Q: Make palindromic string non-palindromic by rearranging its letters.
I just want to know why my solution is failing (wrong answer) for some test cases when i submit the code. I am sure there is an easy solution such as sorting the whole string?
void makeNonPalindrome(string& s)
{
bool ans = false;
int l = s.length();
if(l % 2 == 0)
{
for(int i = l/2; i < l; i++)
{
if(s[l/2 - 1] != s[i])
{
swap(&s[l/2 - 1],&s[i]);
ans = true;
break;
}
}
if(!ans)
{
for(int i = 0; i < l/2-1; i++)
{
if(s[l/2 - 1] != s[i])
{
ans = true;
break;
}
}
}
}
else
{
for(int i = l/2 + 1; i < l; i++)
{
if(s[l/2 - 1] != s[i])
{
swap(&s[l/2 - 1],&s[i]);
ans = true;
break;
}
}
if(!ans)
{
for(int i = 0; i < l/2-1; i++)
{
if(s[l/2 - 1] != s[i])
{
ans = true;
break;
}
}
}
if(!ans)
{
if(s[l/2] != s[0])
{
swap(&s[l/2],&s[0]);
ans = true;
}
}
}
if(ans)
cout << s << '\n';
else
cout << -1 << '\n';
}
Rearranging a palindrome so it become non-palindromic can be done quite fast, by simply trying to swap two adjacent letters in the string if they are different.
For instance, in 'bob', you'd need to find the first distinct adjacent letters (that is b and o in our case), and swap them. The result would then be 'obb', which is not a palindrome.
void makeNonPalindrome(std::string& s) {
char tmp;
for (unsigned i = 0; i < s.length() - 1; i++) {
if (s[i] != s[i+1]) { // then swap s[i] and s[i+1]
tmp = s[i];
s[i] = s[i+1];
s[i+1] = tmp;
std::cout << s << '\n';
return;
}
}
std::cout << -1 << '\n';
}
This is a simpler way to make a palindrome non palindromic.
NB: this function assumes that the input is indeed a palindrome, so if you feed it a string like 'oob', it will output 'bob' which is a palindrome.
Given the input palindrome string s you can just use find_first_not_of to determine if the string can be rearranged into a non-palindrome at all, and if so what characters should be swapped to do this. For example:
const auto foo = s.find_first_not_of(s[0], 1);
If foo == string::npos it means that there isn't a possible non-palindrome rearrangement. Otherwise swap(s[0], s[foo]) will break the palindrome.
Live Example
Related
my attempt was trying to use find form aba / aa to compare each time, but it has some weird edge case , for example, the case "aaaa" was working fine, but "aaaaa" return only "aaaa" was incorrect.
I knew watching the discussion to find the answer for this, but I really want to know where I got wrong with my algorithm and see how can I improve this.
string longestPalindrome(string s) {
if(s.size() == 1)return s;
if(s.size() == 2 &&s[0]!= s[1]){
string x ="";
x+=s[0];
return x;
}
if(s.size() == 2 &&s[0]== s[1])return s;
if(s.size() == 3 && s[0]== s[2])return s;
string ans="";
ans+=s[0];//avoid to_string
for(int i =0; i < s.size();i++){ //c i=1 bb
string temp="";
if(s[i] == s[i+1]){//aa
temp+=s[i];
temp+=s[i+1];
int prev = i-1;
int curr = i+2;// scaacw caac
while(prev-1 != 0 && curr != s.size()){
if(prev== curr){
string x="";
x+=s[prev];
temp.insert(0,x);
temp+=s[curr];
x = "";
}else
break;
prev--;
curr++;
}
if(s[0]==s[s.size()-1]){
string x="";
x+=s[0];
temp.insert(0,x);
temp+=s[s.size()-1];
}
if(temp.size()>ans.size()){
ans = temp;
temp = "";
}
}
else if(s[i] == s[i+2]){//i =3
temp+=s[i];
temp+=s[i+1];
temp+=s[i+2];
int prev = i-1;
int curr = i+3;
while(prev-1 != 0 && curr != s.size()){
if(prev== curr){
string x="";
x+=s[prev];
temp.insert(0,x);
temp+=s[curr];
x = "";
}else
break;
prev--;
curr++;
}
if(s[0]==s[s.size()-1]){
string x="";
x+=s[0];
temp.insert(0,x);
temp+=s[s.size()-1];
}
if(temp.size()>ans.size()){
ans = temp;
temp = "";
}
}
}
return ans;
}
};
So the first problem that you also wrote was that you get "aaaa" instead of "aaaaa". That is because in your for loop, in the first if statement, you check for "aa", so 2 same characters next to eacht other. Then you try something, I guess to go "left" and "right" in the string. But this "if(prev== curr)" does not make much sense, I think you wanted to write "s[prev]" and "s[curr]", because I am not sure how they could ever be equal. Nonetheless, another problem is that when you are looking for the "aa" in the string, you do not find the "middle" "aa", but the first "aa". Then you check if the first letter, which is "a" and the last letter, which is "a" as well are the same. They are, so you add it to your string, hence the "aaaa". If you run it for "1aabc1" it will give you "1aa1", because of the same reasons. You find "aa", then you check if the first equals the last, and you add it.
I think it is a good brain exercise to write these things on your own, but you are not on the correct track yet.
To maybe help a bit, your approach seems to be building the palindrome. So you are not taking substrings and checking, but trying to build it up. You see that correctly, that either you are looking for an "aba"-like or an "aa"-like palindrome. I tried to follow your logic, and came up with the following function:
std::string longestPalindrome2(const std::string& s) {
size_t max_palindrome_length = 0;
int max_palindrome_start = 0;
for (int i = 0; i < s.size(); ++i) {
int down = i - 1;
int up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
down = i;
up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
}
std::string substring = s.substr(max_palindrome_start, max_palindrome_length);
if (substring.empty()) substring = s.substr(0, 1);
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
I tried to do what you did, so building up the palindrome by checking the lower and upper neighbours. Although I am not a 100% sure if this is correct, it seems to work with my test cases.
I also wrote another function, which follows a "top-down" approach instead of your "bottom-up" one. With the test cases, the whole code looks like this:
bool is_it_palindrome(const std::string& str) {
/* loop through the string until the middle is reached */
for (int i = 0; i < str.size() / 2; ++i) {
/* if the mirrored pairs are not the same, it is not a palindrome */
if (str[i] != str[str.size() - 1 - i]) return false;
}
/* all mirrored pairs were the same */
return true;
}
std::string longestPalindrome(const std::string& s) {
/* the first substring will be the whole string itself */
int substring_size = s.size();
/* loop and reduce the substring size in each iteration */
/* so if we assume a string of size 5, we fist take all the possible */
/* 5 long substrings (only one such substring exists, the whole string) */
/* then we take the 4 long substrings, there are 2 of them, then the 3 */
/* long substrings, etc */
while (substring_size > 0) {
/* loop through the characters and get the substring from each character */
/* then check if this substring is a palindrome */
/* since we are looping from the largest substring possible to smaller ones */
/* it will definitely find the largest on first */
for (int i = 0; i < s.size(); ++i) {
if (i + substring_size <= s.size()) {
std::string substring = s.substr(i, substring_size);
if (is_it_palindrome(substring)) {
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
//std::cout << "substring '" << substring << "' is NOT a palindrome " << std::endl;
}
}
--substring_size;
}
return "";
}
std::string longestPalindrome2(const std::string& s) {
size_t max_palindrome_length = 0;
int max_palindrome_start = 0;
for (int i = 0; i < s.size(); ++i) {
/* search for the "aba" style palindrome */
int down = i - 1;
int up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
/* search for the "aa" style palindrome */
down = i;
up = i + 1;
while (down >= 0 && up < s.size()) {
if (s[down] == s[up]) {
int substring_size = up - down + 1;
if (max_palindrome_length < substring_size) {
max_palindrome_length = substring_size;
max_palindrome_start = down;
}
}
else break;
--down;
++up;
}
}
std::string substring = s.substr(max_palindrome_start, max_palindrome_length);
if (substring.empty()) substring = s.substr(0, 1);
std::cout << "substring '" << substring << "' IS a palindrome " << std::endl;
return substring;
}
int main() {
longestPalindrome("aaaaa");
longestPalindrome2("aaaaa");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("1aabc1");
longestPalindrome2("1aabc1");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("21");
longestPalindrome2("21");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("2asddsa1");
longestPalindrome2("2asddsa1");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("a");
longestPalindrome2("a");
std::cout << "----------------------------------------------" << std::endl;
longestPalindrome("abcdefgh");
longestPalindrome2("abcdefgh");
return 0;
}
And the results are the following:
substring 'aaaaa' IS a palindrome
substring 'aaaaa' IS a palindrome
----------------------------------------------
substring 'aa' IS a palindrome
substring 'aa' IS a palindrome
----------------------------------------------
substring '2' IS a palindrome
substring '2' IS a palindrome
----------------------------------------------
substring 'asddsa' IS a palindrome
substring 'asddsa' IS a palindrome
----------------------------------------------
substring 'a' IS a palindrome
substring 'a' IS a palindrome
----------------------------------------------
substring 'a' IS a palindrome
substring 'a' IS a palindrome
I am trying to count the frequency of each word in a given string using two arrays and WITHOUT using Maps or Vectors.
One array to store the words and the other to count the frequency of each word I believe.
I have been teaching myself C++ in my off time and this problem has given me more trouble than I'd like to admit and I've been stuck on it. Maps and Vectors are easier to me, but the problem says specifically not to use them.
This is the code that I used Maps to create pairs but now I need two strings to do the same thing basically.
void wordCounter(string str, string wordsArray[], int countArray[]){
map<string, int> passage;
string word = "";
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' '){
if(passage.find(word) == passage.end()){
passage.insert(make_pair(word, 1));
word = "";
}else{
passage[word]++;
word = "";
}
}else
word += str[i];
}
if(passage.find(word) == passage.end())
passage.insert(make_pair(word, 1));
else
passage[word]++;
for(auto& it : passage) {
cout << it.first << " - " << it.second << endl;
}
}
output would be something like:
thisword - 2
thatword -3
anotherword - 1
etc..
void wordCounter(string str, string wordsArray[], int countArray[]) {
string word = "";
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' ') {
bool found = false;
int spot = 0;
int finder = 0;
for (int j = 0; j < wordsArray.length; j++) {
String wd = wordsArray[j];
if (wd == word) {
spot = finder;
found = true;
break;
}
finder++;
}
if (found) {
countArray[spot] = countArray[spot] + 1;
} else {
// wordsArray[spot + 1] = word
// countArray[spot + 1] = 1
}
word = "";
} else
word += str[i];
}
// to print
// loop through wordsArray and countArray simulatenously
// do something like
for (int simul = 0, simul < wordsArray.length, simul++) {
printf("%s %s\n", wordsArray[simul], countArray[simul]);
}
}
Haven't used C++ in a little bit but essentially if you can't find the word in the arrays, you will want to append the new word to the end of the array and append 1 to the end of the countArray.
Trying to write a program to clean up a string. However for some reason I'm having problem with double spaces. Either it only removes half of the excess spaces, or it just runs forever.
char input[246] = {'\0'};
bool done = false;
int count = 0;
while (!done)
{
cout << "Hello, please enter a string to translate." << endl;
cin.get(input, 246);
}
for (int i = 0; i <= 246; i++)
{
if (input[i] != '\0')
{
count++;
}
}
for (int i = 0; i <= count - 1;) //remove double spaces
{
while (input[i] == ' ' && input[i + 1] == ' ')
{
for (int q = i + 1; q <= (count - 1) - i; q++)
{
input[q] = input[q + 1];
}
count--;
}
else
{
i++;
}
}
You can use std::unique with a custom predicate to remove duplicate spaces:
auto last = std::unique(&input[0], input + strlen(input), [](char const& a, char const &b)
{
return std::isspace(a) && std::isspace(b);
});
*last = '\0'; // Terminate string
From your algorithm, it should be
while (input[i] == ' ' && input[i + 1] == ' ')
{
for (int q = i + 1; q <= count - 1; q++) // Change that line
{
input[q] = input[q + 1];
}
count--;
}
else
{
i++;
}
but alternative solution with std::unique is the way to go.
For the following structure of a trie.
struct Trie {
bool eow; //when a Trie field isWord = true, hence there is a word
char letter;
Trie *letters[27];
};
I'm trying to create a function for an auto completion program, that basically prints out words in a trie given a specific string prefix
Here is what i have:
int wordcheck( TrieNode &node )
{
if (node.isWord == 1) // you have found your word, so return true
{
return 1;
}
for (int i = 0; i < 26; i++)
{
if (node.letters[i] != NULL && wordcheck(*(node.letters[i])))
{
return 1;
}
}
return 0;
}
string find (TrieNode &node, const string &word, string acc)
{
if (word.length() == 0)
{
string x = "";
if (node.isWord == 1){
x = " ";
int check = 1;
for(int i = 0; i < 26; i++)
{
if (node.letters[i] != NULL && wordcheck(*(node.letters[i])))
{
x = x + acc; check = 0; break;
}
}
if(check == 1)
{ return x; }
}
for (int i = 0; i < 26; i++){
if (node.letters[i] != NULL && wordcheck(*(node.letters[i])))
{
char let = (char)(i + (int)'a');
if (x[x.length() - 1 ] == ' ')
{
x = x + acc;
}
x = x + node.letters[i]->letter
+ find(*(node.letters[i]), word, acc + node.letters[i]->letter);
}
}
return x;
}
else if (node.letters[word[0] - 'a'] == NULL)
{ return ""; }
else {
return word[0] + find(*(node.letters[ word[0] - 'a']),
word.substr(1, word.length()-1),
acc + word[0]);
}
}
it seems to work other than the fact it if i give it a long prefix it will print words shorter than the prefix. I used accumulative recursion, and im sure there is a more efficient way of doing this. My question is if anyone could make it so that i return the right strings, or guide me through a easier algorithm if possible?
I'm trying to create a function for an auto completion program, that basically prints out words in a trie given a specific string prefix
I am not going to analyse your program - for me it is too complicated, e.g. I don't get any idea what wordcheck is supposed to do? Why is it not bool but int? Do you really need to check that your sub-trie has any word, do you really have non empty Trie without words in it?
For first - to print all words which begin with the given prefix - you need to go to the node where all these words begin:
TrieNode* TreeNode::get(std::string word)
{
TreeNode* retVal = this;
for (size_t i = 0; i < word.length(); ++i) {
if (Words[i] < 'a' || words[i] > 'z')
throw std::runtime_error("Wrong word");
if (retVal->letters[word[i] - 'a'] != NULL)
retVal = retVal->letters[word[i] - 'a'];
else
return nullptr;
}
return retVal;
}
You need the function which prints all the words from the given node:
void TreeNode::printAll(std::ostream& os, std::string prefix)
{
if (isWord)
os << prefix << "\n";
for (size_t i = 0; i < 26; ++i) {
if (retVal->letters[i] != NULL)
// this recursive call can be replaced with iterative solution with stack
letters[i]->print(os, prefix + char('a' + i));
}
}
And combining these functions - gives you what you want:
void TreeNode::printBeginWith(std::ostream& os, std::string prefix)
{
TreeNode* node = get(prefix);
if (node)
node->printAll(os, prefix);
}
I'm trying to solve a problem that asks to find the largest palindrome in a string up to 20,000 characters. I've tried to check every sub string whether it's a palindrome, that worked, but obviously was too slow. After a little googling I found this nice algorithm
http://stevekrenzel.com/articles/longest-palnidrome. I've tried to implement it, however I can't get it to work. Also the given string contains illegal characters, so I have to convert it to only legal characters and output the longest palindrome with all characters.
Here's my attempt:
int len = original.length();
int longest = 0;
string answer;
for (int i = 0; i < len-1; i++){
int lower(0), upper(0);
if (len % 2 == 0){
lower = i;
upper = i+1;
} else {
lower = i;
upper = i;
}
while (lower >= 0 && upper <= len){
string s2 = original.substr(lower,upper-lower+1);
string s = convert(s2);
if (s[0] == s[s.length()-1]){
lower -= 1;
upper += 1;
} else {
if (s.length() > longest){
longest = s.length();
answer = s2;
}
break;
}
}
}
I can't get it to work, I've tried using this exact algorithm on paper and it worked, please help. Here's full code if you need it : http://pastebin.com/sSskr3GY
EDIT:
int longest = 0;
string answer;
string converted = convert(original);
int len = converted.length();
if (len % 2 == 0){
for (int i = 0; i < len - 1; i++){
int lower(i),upper(i+1);
while (lower >= 0 && upper <= len && converted[lower] == converted[upper]){
lower -= 1;
upper += 1;
}
string s = converted.substr(lower+1,upper-lower-1);
if (s.length() > longest){
longest = s.length();
answer = s;
}
}
} else {
for (int i = 0; i < len; i++){
int lower(i), upper(i);
while (lower >= 0 && upper <= len && converted[lower] == converted[upper]){
lower -= 1;
upper += 1;
}
string s = converted.substr(lower+1,upper-lower-1);
if (s.length() > longest){
longest = s.length();
answer = s;
}
}
}
Okay so I fixed the problems, it works perfectly fine but only if the length of converted string is odd. Please help.
I can see two major errors:
Whether you initialise your upper/lower pointers to i,i or i,i+1 depends on the parity of the palindrome's length you want to find, not the original string. So (without any further optimisations) you'll need two separate loops with i going from 0 to len (len-1), one for odd palindrome lengths and another one for even.
The algorithms should be executed on the converted string only. You have to convert the original string first for it to work.
Consider this string: abc^ba (where ^ is an illegal character), the longest palindrome excluding illegal characters is clearly abcba, but when you get to i==2, and move your lower/upper bounds out by one, they will define the bc^ substring, after conversion it becomes bc, and b != c so you concede this palindrome can't be extended.
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
signed int i=1;
signed int k=0;
int ml=0;
int mi=0;
bool f=0;
while(i<s.length())
{
if(s[i]!=s[i+1])
{
for(k=1;;k++)
{
if(!(s[i-k]==s[i+k] && (i-k)>=0 && (i+k)<s.length()))
{
break;
}
else if(ml < k)
{
ml=k;
mi=i;
f=1;
}
}
}
i++;
}
i=0;
while(i<s.length())
{
if(s[i]==s[i+1])
{
for(k=1;;k++)
{
if(!(s[i-k]==s[k+1+i] && (i-k)>=0 && (k+i)<s.length()))
{
break;
}
else if(ml < k)
{
ml=k;
mi=i;
}
}
}
i++;
}
if(ml < 1)
{
cout << "No Planidrom found";
return 0;
}
if(f==0)
{
cout << s.substr(mi-ml,2*ml+2);
}
else
{
cout << s.substr(mi-ml,2*ml+1);
}
return 0;
}
#biziclop : As you said.. i used 2 while loops. one for even and one for old palindrom string. finally i was able to fix it. thanks for your suggestion.
public void LongestPalindrome()
{
string str = "abbagdghhkjkjbbbbabaabbbbbba";
StringBuilder str1=new StringBuilder();
StringBuilder str2= new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
str1.Append((str[i]));
for (int j = i + 1; j < str.Length; j++)
{
str1.Append((str[j]));
if (Checkpalindrome(str1))
{
str2.Append(str1);
str2.Append(" ");
}
}
str1.Clear();
}
var Palstr = str2.ToString().Split(' ');
var Longestpal = Palstr.Where(a => a.Length >= (Palstr.Max(y => y.Length)));
foreach (var s in Longestpal)
{
Console.WriteLine(s);
}
}
public bool Checkpalindrome(StringBuilder str)
{
string str1 = str.ToString();
StringBuilder str2=new StringBuilder();
var revstr = str1.Reverse();
foreach (var c in revstr )
{
str2.Append(c);
}
if (str1.Equals(str2.ToString()))
{
return true;
}
return false;
}