Compress string of bits in place (C++) [closed] - c++

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm trying to make a function that compresses a string of 0's and 1's. That is, "00010111100101" goes to "30104100101". Should this be a very complicated procedure? My code is looking messy.
The prototype should look like:
static void compress_bitstring(std::string& str) {
// compression algorithm
}

Ok, here's my final answer, with compression of runlengths > 9 and refactoring duplicating code.
template<typename T>
std::string to_string(T t)
{
std::ostringstream str;
str << t;
return str.str();
}
static void compress_bitstring(std::string& str)
{
std::string result;
int count = 0;
char last = 0;
auto add_to_result = [&]{
if (count > 2)
result += to_string(count) + last;
else for (auto loop=0; loop<count; ++loop)
result += last;
};
for (char ch : str)
{
if ((last == 0 || ch == last) && count < 9)
++count;
else {
add_to_result();
count = 1;
}
last = ch;
}
add_to_result();
swap(result, str);
}
int main()
{
std::string str("00010111100101");
compress_bitstring(str);
assert(str == "30104100101");
str = "0001011110010100000000000000000000000001";
compress_bitstring(str);
assert(str == "301041001019090701");
return 0;
}

Here's my approach
update realized my answer could be a lot shorter, and more comprehensive (thanks to Nik Bougalis)
#include <iostream>
#include <sstream>
#include <string>
static void compress_bitstring(std::string& str) {
std::stringstream ss;
std::string cur_num;
int count = 1;
for (int i = 0; i < str.size()-1; ++i){
if (str[i] == str[i+1]){
++count;
cur_num = str[i];
if (i == str.size()-2){
ss << count << cur_num;
} else{
if (count == 9){
ss << count << cur_num;
count = 0;
}
}
} else if (count > 2){
ss << count << cur_num;
count = 1;
if (i == str.size()-2){
ss << str[i+1];
}
} else if (i != str.size()-2){
for (int j = 0; j < count; ++j){
ss << str[i];
}
count = 1;
} else{
ss << str[i] << str[i];
}
}
str = ss.str();
}
static void decode_bitstring(std::string& str){
int i = 0;
int count;
std::stringstream ss;
while (i < str.size()){
std::stringstream to_int;
to_int << str[i];
to_int >> count;
if (count > 1){
for (int j = 0; j < count; ++j){
ss << str[i+1];
}
i = i + 2;
} else{
ss << str[i];
++i;
}
}
str = ss.str();
}
int main(){
std::string binary_num = "0001011110010100000000000000000000000001";
std::cout << binary_num << '\n';
compress_bitstring(binary_num);
std::cout << binary_num << '\n';
decode_bitstring(binary_num);
std::cout << binary_num << '\n';
return 0;
}
edit 2 I also gave a decoder option.
output:
nero#ubuntu:~/learn$ ./lc
0001011110010100000000000000000000000001
301041001019090701
0001011110010100000000000000000000000001

template<typename T>
std::string to_string(T t)
{
std::ostringstream str;
str << t;
return str.str();
}
static void compress_bitstring(std::string& str)
{
std::string result;
int count = 0;
char last = 0;
for (char ch : str)
{
if (last == 0 || ch == last)
++count;
else {
if (count > 2)
result += to_string(count) + last;
else for (auto loop=0; loop<count; ++loop)
result += last;
count = 1;
}
last = ch;
}
if (count > 2)
result += to_string(count) + last;
else for (auto loop=0; loop<count; ++loop)
result += last;
swap(result, str);
}
int main()
{
std::string str("00010111100101");
compress_bitstring(str);
assert(str == "30104100101");
return 0;
}

std::string compress1(std::string &s) {
char c = s[0];
int counter = 1;
std::stringstream ss;
for (size_t i = 1; i < s.length(); i++) {
if (c != s[i]) {
if (counter > 2) ss << counter;
if (counter == 2) ss << c;
ss << c;
counter = 0;
}
c = s[i];
counter++;
}
if (counter > 2) ss << counter;
if (counter == 2) ss << c;
ss << c;
return ss.str();
}
Output:
compress1("00010111100101"): 30104100101
compress1("000101111001011"): 301041001011

Here I post my logic which may help you.
#include<string.h>
int main(){
char* str ="00010111100101";
char* result = malloc(strlen(str)+1);
int i = 0;
char *q;
char * p = str;
result[i++] = str[0];
do{
q = strchr(str,str[0]=='0'?'1':'0'); //Searching for the opposite char.
result[i++] = (q == '\0'? str+strlen(str) : q ) - p + '0'; // Storing the difference in result
str = p = q; //updating str and p
}while(q != '\0');
result[i] = '\0';
printf("Compressed Result = %s",result);
return 0;
}
http://codepad.org/Qn8NEuB7

Related

a+= b faster than a = a+b. The logic behind the two operations

I was working on a leetcode problem to reverse a string such that such that if Input is The boy is mad, the output should be mad is boy The. I solved it but the solution seemed poor as I was running through the string a lot of times. On submitting the code, the time taken to compute all the test cases was ~250ms, and I believed it as I knew this was the worst possible code. I looked up one of the answers and ran that code, it ran in like 4ms. I wrote the same code again, but this time I used a = a + b; instead of a += b;, and the test cases again took around 250ms. So, I want to know what is the difference between these two and why does it take a long time on one when compared to the other.
PS - In my original (poor) code, on changing a = a + b to a += b, the code ran in like 8ms.
Here are the codes:
1. slow code(runtime - ~250 ms)
string reverseWords(string s) {
if(s.length() == 0)
{
return s;
}
int itr = 0;
// Remove white spaces from front
while(s[itr] == ' ')
{
itr++;
}
s = s.substr(itr,s.length()-itr);
if(s.length() == 0)
{
return s;
}
// Remover white spaces from back
for(int i = s.length()-1; i>= 0 ;i--)
{
while(s[i] == ' ')
{
s = s.substr(0,i);
i--;
//cout << s << endl;
}
break;
}
if(s.length() == 0)
{
return s;
}
cout << s << endl;
// Remove more than one space in the middle
for(int i = 0;i<s.length()-1;i++)
{
if(s[i] == ' ' && s[i+1] == ' ')
{
string temp;
temp = s.substr(0,i);
s = s.substr(i+1,s.length());
s = temp + s;
i--;
//cout << s <<endl;
}
}
string res = "";
reverse(s.begin(),s.end());
int start = 0;
for(int i = 0;i <s.length();i++)
{
start = i;
if(s[i] == ' ')
continue;
while(s[i] != ' ' && i < s.length())
{
i++;
// cout << i << " ";
}
// cout << endl;
string temp = s.substr(start,i-start);
reverse(temp.begin(),temp.end());
if(i != s.length())
res = res + temp + " ";
else
res = res + temp;
//cout << temp << endl;
}
return res;
}
Fast code(4ms)
string reverseWords(string s)
{
stack<string> elements;
std::istringstream stream(s);
std::string word;
while (stream >> word)
{
elements.push(word);
}
string strReturn("");
int stackSize = elements.size();
int i = 0;
while( !elements.empty())
{
i++;
strReturn += elements.top();
elements.pop();
if( i != stackSize)
{
strReturn += " ";
}
}
return strReturn;
}
Re written Fast code(250ms)
string reverseWords(string s) {
// Check the first available Accepted solution to see the shitiest solution for the code
stack<string> availableWords;
istringstream stream(s);
string word;
while(stream >> word)
{
availableWords.push(word);
}
int len = availableWords.size();
string res = "";
int i = 0;
while(!availableWords.empty())
{
res += availableWords.top();
availableWords.pop();
i++;
if(i != len)
{
res += " ";
}
}
return res;
}

How to get a substring by deleting minimum number of characters?

In this question, we take 2 strings as input say s1 and s2.
Now, first we need to check if s2 is a subsequence of s1. If not, print no.
But if it is, we need to print the minimum number of characters to be deleted from s1 to get s2.
Eg- thistext text
Here, text can be directly found without deleting any characters so the answer is 0.
Eg- cutefriendship crisp
In this case, the answer is 9.
What I've done so far,
#include <bits/stdc++.h>
using namespace std;
int checkIfSub(string s1, string s2, int m, int n)
{
int j = 0;
for(int i = 0; i < m && j < n; i++)
if(s1[i] == s2[j])
j++;
if(j == n)
return 0;
else
return 1;
}
int check(string s1, string s2)
{
int count = 0; string s3;
if(checkIfSub(s1, s2, s1.length(), s2.length()) == 1 || s2.length() > s1.length())
{
cout << "NO\n"; return 0;
}
int j = 0;
for(int i = 0; i < s1.length(); i++)
{
if(s1[i] == s2[j])
{
s3[j] = s1[j];
j++; continue;
}
count++;
}
cout << "YES " << count << "\n";
return 0;
}
int main() {
string s1, s2;
cin >> s1 >> s2;
check(s1, s2);
return 0;
}
My code works well for the second example, but fails the first case.
(This was a question asked in some interview I read online.)
Try something like this:
#include <iostream>
#include <string>
using namespace std;
bool check(const string &s1, const string &s2, int &minToDelete)
{
minToDelete = 0;
bool anySubSeqFound = false;
if (s2.empty())
return false;
string::size_type first = 0;
while ((first = s1.find(s2[0], first)) != string::npos)
{
int numDeleted = 0;
bool isSubSeq = true;
string::size_type next = first + 1;
for(string::size_type j = 1; j < s2.size(); ++j)
{
string::size_type found = s1.find(s2[j], next);
if (found == string::npos)
{
isSubSeq = false;
break;
}
numDeleted += (found - next);
next = found + 1;
}
if (isSubSeq)
{
if (anySubSeqFound)
{
if (numDeleted < minToDelete)
minToDelete = numDeleted;
}
else
{
anySubSeqFound = true;
minToDelete = numDeleted;
}
}
++first;
}
return anySubSeqFound;
}
int main()
{
int minToDelete;
if (check("thistext", "text", minToDelete))
cout << "yes, delete " << minToDelete << endl;
else
cout << "no" << endl;
if (check("cutefriendship", "crisp", minToDelete))
cout << "yes, delete " << minToDelete << endl;
else
cout << "no" << endl;
}
Live Demo

C++: Change of base function (i.e. hex to octal, decimal, etc.) - Output slightly off for hex values

I need to create a generic function that changes from any starting base, to any final base. I have everything down, except my original function took (and takes) an int value for the number that it converts to another base. I decided to just overload the function. I am Ok with changing between every base, but am slightly off when using my new function to take in a string hex value.
The code below should output 1235 for both functions. It does for the first one, but for the second, I am currently getting 1347. Decimal to Hex works fine - It's just the overloaded function (Hex to anything else) that is slightly off.
Thanks.
#include <iostream>
#include <stack>
#include <string>
#include <cmath>
using namespace std;
void switchBasesFunction(stack<int> & myStack, int startBase, int finalBase, int num);
void switchBasesFunction(stack<int> & myStack, int startBase, int finalBase, string s);
int main()
{
stack<int> myStack;
string hexNum = "4D3";
switchBasesFunction(myStack, 8, 10, 2323);
cout << endl << endl;
switchBasesFunction(myStack, 16, 10, hexNum);
return 0;
}
void switchBasesFunction(stack<int> & myStack, int startBase, int finalBase, int num)
{
int totalVal = 0;
string s = to_string(num);
for (int i = 0; i < s.length(); i++)
{
myStack.push(s.at(i) - '0');
}
int k = 0;
while (myStack.size() > 0)
{
totalVal += (myStack.top() * pow(startBase, k++));
myStack.pop();
}
string s1;
while (totalVal > 0)
{
int temp = totalVal % finalBase;
totalVal = totalVal / finalBase;
char c;
if (temp < 10)
{
c = temp + '0';
s1 += c;
}
else
{
c = temp - 10 + 'A';
s1 += c;
}
}
for (int i = s1.length() - 1; i >= 0; i--)
{
cout << s1[i];
}
cout << endl << endl;
}
void switchBasesFunction(stack<int> & myStack, int startBase, int finalBase, string s)
{
int totalVal = 0;
for (int i = 0; i < s.length(); i++)
{
myStack.push(s.at(i) - '0');
}
int k = 0;
while (myStack.size() > 0)
{
totalVal += (myStack.top() * pow(startBase, k++));
myStack.pop();
}
string s1;
while (totalVal > 0)
{
int temp = totalVal % finalBase;
totalVal = totalVal / finalBase;
char c;
if (temp < 10)
{
c = temp + '0';
s1 += c;
}
else
{
c = temp - 10 + 'A';
s1 += c;
}
}
for (int i = s1.length() - 1; i >= 0; i--)
{
cout << s1[i];
}
cout << endl << endl;
}
Sorry, but I'm having issues understanding your code, so I thought I'd simplify it.
Here's the algorithm / code (untested):
void convert_to_base(const std::string& original_value,
unsigned int original_base,
std::string& final_value_str,
unsigned int final_base)
{
static const std::string digit_str =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ((original_base > digit_str.length()) || (final_base > digit_str.length())
{
std::cerr << "Base value exceeds limit of " << digit_str.length() << ".\n";
return;
}
// Parse string from right to left, smallest value to largest.
// Convert to decimal.
unsigned int original_number = 0;
unsigned int digit_value = 0;
int index = 0;
for (index = original_value.length(); index > 0; --index)
{
std::string::size_type posn = digit_str.find(original_value[index];
if (posn == std::string::npos)
{
cerr << "unsupported digit encountered: " << original_value[index] << ".\n";
return;
}
digit_value = posn;
original_number = original_number * original_base + digit_value;
}
// Convert to a string of digits in the final base.
while (original_number != 0)
{
digit_value = original_number % final_base;
final_value_str.insert(0, 1, digit_str[digit_value]);
original_number = original_number / final_base;
}
}
*Warning: code not tested via compiler.**

C++ Anagram Maker Error

I have been trying to make an anagram maker, using a textBox (encryption_text) for input, which the text is "Hello World", and the output textBox (encrypted_text) which receives the text:
"ellllloooo
WWWWWWooooooorrrrrrrrllllllllldddddddddd".
I also have a textBox called 'anag_used', which should record the used number/location in the string to encrypt.
Have I over complicated it, or is there an error?
Thanks :)
Here is my code:
void anagram()
{
string toanagram = marshal_as<string>(encryption_text->Text);
string out;
int k;
System::String^ rndstr;
System::String^ ktostr;
ostringstream kstr;
anag_used->Clear();
for (int i = 0; i < toanagram.size(); ++i)
{
anag_used->Text += "\n";
int rnd = 0 + rand() % toanagram.size();
ostringstream rndtostr;
rndtostr << rnd;
rndstr = gcnew System::String(rndtostr.str().c_str());
for (int l = 0; l < i; ++l)
{
if (anag_used->Lines[l] == rndstr)
{
k = rnd;
kstr << k;
ktostr = gcnew System::String(kstr.str().c_str());
for (System::String^ j = anag_used->Lines[l]; j == ktostr; k = 0 + rand() % toanagram.size())
{
kstr << k;
ktostr = gcnew System::String(kstr.str().c_str());
if (anag_used->Lines[l] == ktostr)
{
//Do someting if you want
}
else
{
out += toanagram[k];
anag_used->Lines[l] = ktostr;
}
}
}
else
{
out += toanagram[i];
anag_used->Lines[i] = rndstr;
}
}
}
encrypted_text->Text = marshal_as<System::String^>(out);
}
EDIT: FOUND A MUCH SIMPLER WORKING CODE
#include <algorithm>
.
string toanagram = marshal_as<string>(encryption_text->Text);
sort(toanagram.begin(), toanagram.end());
encrypted_text->Text = marshal_as<System::String^>(toanagram);
This works for console, but you could implement it in C++/CLI quite easily
#include <iostream>
#include <sstream>
#include <vector>
#include <ctime>
void str_vect(std::vector<const char>* v, std::string& s)
{
for (int i = 0; i < s.length(); ++i)
{
v->push_back(s[i]);
}
}
int main()
{
for (;;)
{
std::cout << "Please enter the word / phrase\n";
std::string word;
std::getline(std::cin, word);
std::vector<const char> word_split;
str_vect(&word_split, word);
int sz = word_split.size();
std::string anagram;
for (int i = 0; i < sz; ++i)
{
srand(time(NULL));
int r = (rand() % (word_split.size() - 0)) + 0;
anagram += word_split[r];
word_split.erase((word_split.begin()) + r);
}
system("cls");
std::cout << "Please guess the anagrammed phrase / word - '" << anagram << "'\n";
int max_tries = 3;
int tries = max_tries;
for (int i = 0; i <= max_tries; ++i)
{
std::string guess;
std::getline(std::cin, guess);
if (guess != word)
{
tries--;
if (tries == 0)
{
std::cout << "You have ran out of tries. The answer was: " << word << "\n";
break;
}
std::cout << tries << ((tries == 1) ? " try" : " tries") << " left\n";
}
else
{
std::cout << "Correct!\n";
break;
}
}
}
}
#include <algorithm>
.
string toanagram = marshal_as<string>(encryption_text->Text);
sort(toanagram.begin(), toanagram.end());
encrypted_text->Text = marshal_as<System::String^>(toanagram);

C++: Implementing merge sort from scratch

I was trying to test my self and wanted to write mergesort, without actually looking up any code online, and to do it in a certain time period. I am stuck at this point where I cannot simply understand what I am doing wrong, since merge sort, as much as i remember, is to divide the strings up to the point where string is only 1 character and later on merge them back together. The code I've written below tries to do the exact thing. I was wondering whether I got the concept wrong, or just my implementation?
string merge(string str1, string str2) {
string final = "";
int i = 0, j = 0;
bool fromStr1 = false;
while(true) {
if(str1[i] < str2[j]) {
final += str1[i];
i++;
if(i == str1.size()) {
break;
}
}
else {
final += str2[j];
j++;
if(j == str2.size()) {
break;
fromStr1 = true;
}
}
}
if(fromStr1) {
for(int t = i; t < str1.size(); t++) {
final += str1[t];
}
}
else {
for(int t = j; t < str2.size(); t++) {
final += str2[t];
}
}
return final;
}
string mergeSort(string str1, int start, int end) {
if(end - start == 1)
return str1;
else {
int pivot = (end - start) / 2;
string newStr1 = mergeSort(str1, start, pivot);
string newStr2 = mergeSort(str1, pivot + 1, end);
return merge(newStr1, newStr2);
}
}
Note the changes:
#include <iostream>
#include <string>
using namespace std;
string merge(string str1, string str2) {
string final = "";
int i = 0, j = 0;
bool fromStr1 = false;
while (true) {
if (i >= (int)str1.size()) {
break;
}
if (j >= (int)str2.size()) {
fromStr1 = true; // changed the order of this with break!
break;
}
if (str1[i] < str2[j]) {
final += str1[i];
i++;
}
else {
final += str2[j];
j++;
}
}
if (fromStr1) {
for (int t = i; t < (int)str1.size(); t++) {
final += str1[t];
}
}
else {
for(int t = j; t < (int)str2.size(); t++) {
final += str2[t];
}
}
return final;
}
string mergeSort(string str1) {
int len = str1.size();
if (len <= 1)
return str1;
else {
string newStr1 = mergeSort(str1.substr(0, len / 2));
string newStr2 = mergeSort(str1.substr(len / 2, len - len / 2));
return merge(newStr1, newStr2);
}
}
int main()
{
cout << '"' << mergeSort("") << '"' << endl;
cout << '"' << mergeSort("a") << '"' << endl;
cout << '"' << mergeSort("ba") << '"' << endl;
cout << '"' << mergeSort("132") << '"' << endl;
cout << '"' << mergeSort("4321") << '"' << endl;
cout << '"' << mergeSort("54321") << '"' << endl;
return 0;
}
Output (ideone):
""
"a"
"ab"
"123"
"1234"
"12345"
This doesn't look right:
int pivot = (end - start) / 2;
string newStr1 = mergeSort(str1, start, pivot);
string newStr2 = mergeSort(str1, pivot + 1, end);
Don't you mean pivot=(end+start)/2? Or else mergeSort(str1, start, start+pivot) and mergeSort(str1, start+pivot+1, end)?
EDIT:
And your merge doesn't cope well with empty strings. You should have tested this function before hooking it up to mergeSort.
It's been ages since I used C++, but doesn't break immediately exit the loop? Because fromStr1 = true; is never reached in that case.