Repeated Function call in the following program in C++ - c++

I have to write a programs that takes an input of string which has some '$' and digits. The output of the program is set of all possible strings where the '$ in the string is replaced by all the other digits.
I have written the following code for it.
#include<bits/stdc++.h>
using namespace std;
int numberOf(string in)
{
int count = 0;
for(int i = 0; i <= in.size()-1; i++)
if(in[i] == '$')
count++;
return count;
}
void solve(string in, string in1, vector <string> &s,
int index)
{
if(numberOf(in) == 0)
{
s.push_back(in);
return;
}
if(index == in.size())
{
return;
}
if(in1.empty())
{
return;
}
else
{
if(in[index] == '$')
{
string in2 = in;
in2[index] = in1[0];
string in3 = in1;
in3.erase(in3.begin());
solve(in2, in1, s, index+1);
solve(in, in3, s, index);
return;
}
else
{
solve(in, in1, s, index+1);
return;
}
}
}
void replaceDollar(string in)
{
string in1 = in;
int count = 0;
for(int i = 0; i <= in.size()- 1; i++)
{
if(in[i] != '$')
{
in1.push_back(in[i]);
count++;
}
}
count = in.size() - count;
cout << "Number is " << count << "\n";
vector <string> s;
solve(in, in1, s, 0);
for(auto i = s.begin(); i != s.end(); i++)
cout << *i << " ";
cout << "\n";
}
int main()
{
int t;
cin >> t;
while(t--)
{
string in;
cin >> in;
replaceDollar(in);
}
return 0;
}
For following input
1
$45
The expected output should be
445 545
But it returns
445 545 445 545
Can anyone please explain why is it outputting repeated strings?
Also can anyone suggest a better approach to this question?
Thanks in advance!

Assuming that this is homework:
Start over. Your code is way too complex for this problem.
I would treat everything as type char
Loop/iterate over said string, using std::string::replace() to replace each instance of $ with each digit.
-- If your teacher doesn't want you using std libraries, then add another loop and compare yourself.
3a. Of course, add a check, so that you don't replace $ with $
Create a new copy of the string on each iteration.
Print each to stdout as you create them.
See this post:
How to replace all occurrences of a character in string?
p.s. Pro tip: don't use using namespace. Use the full namespace in your calls; e.g.:
Bad
using namespace std;
string = "hello world";
Good
std::string = "hello world";

Related

Replacing spaces in a string with increasing numbers

I need a program to take a string and replace spaces with increasing numbers.
#include <cstring>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
// Get the String
string str = "this essay needs each word to be numbered";
int num = 1;
string x = num;
int i = 0;
// read string character by character.
for (i < str.length(); ++i) {
// Changing the loaded character
// to a number if it's a space.
if (str[i] == ' ') {
str[i] = x;
++num
}
}
// testing outputs
cout << str << endl;
cout << num << endl;
ofstream file;
file.open ("numbered.txt");
file << str;
file.close();
return 0;
}
I had it at the point where it could replace spaces with a letter or symbol and save to a new file but when I tried to make it a number it stopped working. I would need it to say "this1essay2needs3each4word5to6be7numbered
For ease and clarity, change your approach.
Put the string into an istringstream
Extract each space-separated substring and place into an std::vector<string>
Feed the contents of the vector into a stringstream and
use std::to_string(num) to add the numbers between the substrings
e.g.:
std::string str = "this essay needs each word to be numbered";
int num = 1;
std::istringstream istr(str);
std::string temp;
std::vector<std::string> substrs;
while (istr >> temp)
{
substrs.push_back(temp);
}
std::stringstream ostr;
for (auto&& substr : substrs)
{
ostr << substr << std::to_string(num++);
}
Let's break the problem down into parts. We can make a SpaceReplacer object that does the replacement. It has an Output, which it can use as a function to output characters:
template<class Output>
struct SpaceReplacer {
Output output;
int num_spaces;
void input(char c) {
if(c == ' ') {
auto num_as_string = std::to_string(num_spaces);
num_spaces += 1;
for(char digit : num_as_string) {
output(digit);
}
}
else {
output(c);
}
}
};
Every time you input a character, it either outputs the character you input, or it outputs the digits of the number (if the character was a space).
We should write a helper function to make SpaceReplacers:
template<class Output>
SpaceReplacer<Output> makeReplacer(Output output_func) {
return SpaceReplacer<Output>{output_func, 0};
}
Reading one string, returning new string
It's now easy to write a function that replaces spaces in a string.
std::string replaceSpaces(std::string const& input) {
std::string output_string;
// We output chars by appending them to the output string
auto output_func = [&](char c) { output_string += c; };
auto replacer = makeReplacer(output_func);
for(char c : input) {
replacer.input(c);
}
return output_string;
}
Reading input from file, replacing spaces and returning a string
Because we wrote a really generic SpaceReplacer class, we can modify the function so that it'll read input directly from a FILE*
std::string replaceSpaces(FILE* file) {
std::string output_string;
auto output_func = [&](char c) { output_string += c; };
auto replacer = makeReplacer(output_func);
while(true) {
int input_char = fgetc(file);
if(input_char == EOF) {
break;
}
replacer.input(input_char);
}
return output_string;
}
Reading input from one file, immediately appending it to different file with spaces replaced
We can also read directly from one file, and output directly to another file, with no delay. This might be useful if you were processing a very large amount of data.
void replaceSpaces(FILE* input_file, FILE* output_file) {
auto output_func = [=](char c) { fputc(c, output_file); };
auto replacer = makeReplacer(output_func);
while(true) {
int input_char = fgetc(input_file);
if(input_char == EOF) {
break;
}
replacer.input(input_char);
}
}
In this case, you need to use another string for the result.
#include <cstring>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
// Get the String
string result, str = "this essay needs each word to be numbered qwe qwe wqe qwe qwe qwe q";
int num = 0;
int i;
// read string character by character.
for (i=0; i < str.length(); i++) {
// Changing the loaded character
// to a number if it's a space.
if (str[i] == ' ')
result+=std::to_string(++num);
else
result+=str[i];
}
// testing outputs
cout<<result<<endl;
cout<<num;
ofstream file;
file.open ("numbered.txt");
file << result;
file.close();
return 0;
}
You have to replace it with a character, not by a number.
str[i] = num+'0';

Adding Space to String at certain places in C++

I am having trouble figuring out the process to add a space in a string at capital letters in C++. If I have a string "HelloWorld", how do I convert that to "Hello World"?
I've tried using substrings and the isupper function but I can't get anything to work.
Edit: this is the code I have. I don't understand why in = newname is not valid code.
string breakStringAtCaps(string in) {
string newname[10];
int j = 0;
for (int i = 0; i < in.size(); i++) {
if (isupper(in[i]) && i != 0) {
newname[j] = " ";
j++;
}
newname[j] = in[i];
j++;
}
in = newname;
return in;
}
You can do it like this:
string breakStringAtCaps(const string& in)
{
string newname;
for(int i = 0; i < in.size(); i++)
{
if(isupper(in[i]) && i != 0)
newname += " ";
newname += in[i];
}
return newname;
}
You are thinking right in thinking substr, but you implementation is a bit off. If creating an new string containing the contents of the original and inserting a ' ' (space) before each upper-case letter (not including the first), you can seed the new string with the first character of the original using substr(0,1) and then use an auto ranged for loop and substr(1) to evaluate each character after the first.
The loop along with a check of isupper() is basically all you need, e.g.
#include <iostream>
#include <string>
#include <cctype>
int main (int argc, char **argv) {
std::string s = argc > 1 ? argv[1] : "HelloWorld",
snew = s.substr (0,1);
if (s.size() > 0)
for (auto& c : s.substr(1)) {
if (std::isupper (c))
snew += " ";
snew += c;
}
std::cout << snew << '\n';
}
(the program will use "HelloWorld" by default if no string is given as an argument on the command line, otherwise the string given on the command line is used)
Example Use/Output
$ ./bin/spacebeforeupper
Hello World
Or with a string given as an argument:
$ ./bin/spacebeforeupper MyDogHasFleas
My Dog Has Fleas
You can iterate through the strin characters, check if it is a cap and insert a ' ' before if it is one:
It should look like:
for(int i=0; i < str.length(); i++)
{
if (str[i]>='A' && str[i]<='Z')
{
if (i != 0)
cout << " ";
cout << str[i];
}
else
{
cout << str[i];
}
}
I just give a implementation, maybe not the best solution:
#include <string>
#include <ctype.h>
void breakStringAtCaps(std::string& in) {
std::string::const_iterator it = in.begin();
while(it != in.end()) {
if(it != in.begin() && isupper(*it)) {
in.insert(it, ' ');
it += 2;
}
else
++it;
}
}
//
#include <iostream>
int main(int argc, char** argv) {
std::string str("HelloWorld;");
breakStringAtCaps(str);
std::cout << str.c_str() << std::endl;
return 0;
}
and in your code,
string newname[10];
here 'newname' is a string array's name. you should not assign the name of array to a string instance.
and newname[i] means the i-th string of the array, not the i-th char of a string named 'newname' as you desired.

Checking if alphabetic string is a palindrome in C++

I've attempted to write a code that checks whether or not a string is a palindrome. Here is the code:
#include <iostream>
#include <string>
using namespace std;
bool pal(string str)//This block of code checks if input string is a palindrome
{
bool valid;
int i;
for (i = 0; i < str.length(); i++)
{
if (str[-i] == str[i])
{
valid = true;
}
else
{
valid = false;
}
}
return valid;
}
int main()
{
string s;
cin >> s;
if (!pal(s))
{
cout << "NO" << endl;
}
else
{
cout << "YES" << endl;
}
return 0;
}
Currently I am getting "Debug Assertion Fail" error.
str[-i] == str[i]
is a problem since negative indices are not valid indices in C++.
You need to change the strategy a little bit.
bool pal(string str)
{
int i = 0;
int j = str.length() - 1;
for ( ; i < j; ++i, --j)
{
if (str[i] != str[j])
{
// No need for any more checks.
return false;
}
}
// If we come here, the string is a palindrome.
return true;
}
C++ Provides us with an inbuilt function reverse() which can be used to reverse the Input string and compare it with un reversed string and print the output. The code goes as follows.
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
string str;
cin>> str;
string rev;
rev = str;
reverse(str.begin(), str.end()); // string reverse operation
if(rev == str){
cout<<"YES"<<endl; // Prints "Yes" if string is palindrome
}else{
cout<<"NO"<<endl; // Prints "No" if string is not palindrome
}
return 0;
}

Sorting and writing to file dates

#include <iostream> // std::cout
#include <cstdlib>
#include <climits>
#include <algorithm>
#include <cmath>
#include <fstream>
using namespace std;
struct student{
int ID; // ID
string firstname; // first name
string lastname; // last name
int date; // YYMMDD
static bool sort_date(student a, student b){
long data1;
long data2;
data1 = a.date;
data2 = b.date;
if(data1 < 150000){
data1 += 20000000;
}
else{
data2 += 19000000;
}
if(data2 < 150000){
data2 += 20000000;
}
else{
data1 += 19000000;
}
return data1 < data2;
}
};
bool is_num(const string &s);
void input_year(student &students);
int length_of_int(int x);
int main(){
student students[5];
students[0].date = 000101;
students[1].date = 951230;
students[2].date = 570509;
students[3].date = 120915;
students[4].date = 020324;
stable_sort(students, students + 5, student::sort_date);
ofstream file;
file.open("sort_date.txt");
for(int i = 0; i < 5; i++){
file << students[i].date << endl;
}
return 0;
}
void input_year(student &students){
while(true){
string input;
cin >> input;
if(is_num(input)){
students.date = atoi(input.c_str());
if(length_of_int(students.date) != 6){
cout << "Error, try again." << endl;
}
else{
//
break;
}
}
else{
cout << "Error, try again." << endl;
}
}
}
bool is_num(const string &s){
string::const_iterator it = s.begin();
while(it != s.end() && isdigit(*it)){
++it;
}
return !s.empty() && it == s.end();
}
int length_of_int(int input){
int length = 0;
while(input > 0){
length++;
input /= 10;
}
return length;
}
This is my code above and I'm not sure what else to do to sort the dates.. I've been working on this for a while and can't get it right. I need help, preferably a code which solves my problem.
Basically, the type of date is "YYMMDD", so in sort_date function, I make those integers in format of "YYYYMMDD" and then sort them and then again become YYMMDD. However, the sorting is somehow wrong.. I tried several times, and when writing a date like "010101" in the file, it removes the first "0", so I am looking for help with those two problems. Any help is appreciated.
If a leading 0 is significant, then you don't have an int, but a string. You can check whether strings are sorted using < just as well as you can ints.
Also look at your if-else for adding 19 or 20; you check data1 but then modify data2 (and vice versa)....
Starting a number with 0 in C means that the base should be interpreted as octal (base 8 rather than 10): the literal 020324 will be interpreted as the decimal number 8404.
First convert dates to time_t or tm, then use datetime library (http://en.wikipedia.org/wiki/C_date_and_time_functions)

Complex algorithm to extract numbers/number range from a string

I am working on a algorithm where I am trying the following output:
Given values/Inputs:
char *Var = "1-5,10,12,15-16,25-35,67,69,99-105";
int size = 29;
Here "1-5" depicts a range value, i.e. it will be understood as "1,2,3,4,5" while the values with just "," are individual values.
I was writing an algorithm where end output should be such that it will give complete range of output as:
int list[]=1,2,3,4,5,10,12,15,16,25,26,27,28,29,30,31,32,33,34,35,67,69,99,100,101,102,103,104,105;
If anyone is familiar with this issue then the help would be really appreciated.
Thanks in advance!
My initial code approach was as:
if(NULL != strchr((char *)grp_range, '-'))
{
int_u8 delims[] = "-";
result = (int_u8 *)strtok((char *)grp_range, (char *)delims);
if(NULL != result)
{
start_index = strtol((char*)result, (char **)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(NULL != result)
{
end_index = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(start_index <= end_index)
{
grp_list[i++] = start_index;
start_index++;
}
}
else if(NULL != strchr((char *)grp_range, ','))
{
int_u8 delims[] = ",";
result = (unison_u8 *)strtok((char *)grp_range, (char *)delims);
while(result != NULL)
{
grp_list[i++] = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
}
But it only works if I have either "0-5" or "0,10,15". I am looking forward to make it more versatile.
Here is a C++ solution for you to study.
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int ConvertString2Int(const string& str)
{
stringstream ss(str);
int x;
if (! (ss >> x))
{
cerr << "Error converting " << str << " to integer" << endl;
abort();
}
return x;
}
vector<string> SplitStringToArray(const string& str, char splitter)
{
vector<string> tokens;
stringstream ss(str);
string temp;
while (getline(ss, temp, splitter)) // split into new "lines" based on character
{
tokens.push_back(temp);
}
return tokens;
}
vector<int> ParseData(const string& data)
{
vector<string> tokens = SplitStringToArray(data, ',');
vector<int> result;
for (vector<string>::const_iterator it = tokens.begin(), end_it = tokens.end(); it != end_it; ++it)
{
const string& token = *it;
vector<string> range = SplitStringToArray(token, '-');
if (range.size() == 1)
{
result.push_back(ConvertString2Int(range[0]));
}
else if (range.size() == 2)
{
int start = ConvertString2Int(range[0]);
int stop = ConvertString2Int(range[1]);
for (int i = start; i <= stop; i++)
{
result.push_back(i);
}
}
else
{
cerr << "Error parsing token " << token << endl;
abort();
}
}
return result;
}
int main()
{
vector<int> result = ParseData("1-5,10,12,15-16,25-35,67,69,99-105");
for (vector<int>::const_iterator it = result.begin(), end_it = result.end(); it != end_it; ++it)
{
cout << *it << " ";
}
cout << endl;
}
Live example
http://ideone.com/2W99Tt
This is my boost approach :
This won't give you array of ints, instead a vector of ints
Algorithm used: (nothing new)
Split string using ,
Split the individual string using -
Make a range low and high
Push it into vector with help of this range
Code:-
#include<iostream>
#include<vector>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
int main(){
std::string line("1-5,10,12,15-16,25-35,67,69,99-105");
std::vector<std::string> strs,r;
std::vector<int> v;
int low,high,i;
boost::split(strs,line,boost::is_any_of(","));
for (auto it:strs)
{
boost::split(r,it,boost::is_any_of("-"));
auto x = r.begin();
low = high =boost::lexical_cast<int>(r[0]);
x++;
if(x!=r.end())
high = boost::lexical_cast<int>(r[1]);
for(i=low;i<=high;++i)
v.push_back(i);
}
for(auto x:v)
std::cout<<x<<" ";
return 0;
}
You're issue seems to be misunderstanding how strtok works. Have a look at this.
#include <string.h>
#include <stdio.h>
int main()
{
int i, j;
char delims[] = " ,";
char str[] = "1-5,6,7";
char *tok;
char tmp[256];
int rstart, rend;
tok = strtok(str, delims);
while(tok != NULL) {
for(i = 0; i < strlen(tok); ++i) {
//// range
if(i != 0 && tok[i] == '-') {
strncpy(tmp, tok, i);
rstart = atoi(tmp);
strcpy(tmp, tok + i + 1);
rend = atoi(tmp);
for(j = rstart; j <= rend; ++j)
printf("%d\n", j);
i = strlen(tok) + 1;
}
else if(strchr(tok, '-') == NULL)
printf("%s\n", tok);
}
tok = strtok(NULL, delims);
}
return 0;
}
Don't search. Just go through the text one character at a time. As long as you're seeing digits, accumulate them into a value. If the digits are followed by a - then you're looking at a range, and need to parse the next set of digits to get the upper bound of the range and put all the values into your list. If the value is not followed by a - then you've got a single value; put it into your list.
Stop and think about it: what you actually have is a comma
separated list of ranges, where a range can be either a single
number, or a pair of numbers separated by a '-'. So you
probably want to loop over the ranges, using recursive descent
for the parsing. (This sort of thing is best handled by an
istream, so that's what I'll use.)
std::vector<int> results;
std::istringstream parser( std::string( var ) );
processRange( results, parser );
while ( isSeparator( parser, ',' ) ) {
processRange( results, parser );
}
with:
bool
isSeparator( std::istream& source, char separ )
{
char next;
source >> next;
if ( source && next != separ ) {
source.putback( next );
}
return source && next == separ;
}
and
void
processRange( std::vector<int>& results, std::istream& source )
{
int first = 0;
source >> first;
int last = first;
if ( isSeparator( source, '-' ) ) {
source >> last;
}
if ( last < first ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
while ( first != last ) {
results.push_back( first );
++ first;
}
results.push_back( first );
}
}
The isSeparator function will, in fact, probably be useful in
other projects in the future, and should be kept in your
toolbox.
First divide whole string into numbers and ranges (using strtok() with "," delimiter), save strings in array, then, search through array looking for "-", if it present than use sscanf() with "%d-%d" format, else use sscanf with single "%d" format.
Function usage is easily googling.
One approach:
You need a parser that identifies 3 kinds of tokens: ',', '-', and numbers. That raises the level of abstraction so that you are operating at a level above characters.
Then you can parse your token stream to create a list of ranges and constants.
Then you can parse that list to convert the ranges into constants.
Some code that does part of the job:
#include <stdio.h>
// Prints a comma after the last digit. You will need to fix that up.
void print(int a, int b) {
for (int i = a; i <= b; ++i) {
printf("%d, ", i);
}
}
int main() {
enum { DASH, COMMA, NUMBER };
struct token {
int type;
int value;
};
// Sample input stream. Notice the sentinel comma at the end.
// 1-5,10,
struct token tokStream[] = {
{ NUMBER, 1 },
{ DASH, 0 },
{ NUMBER, 5 },
{ COMMA, 0 },
{ NUMBER, 10 },
{ COMMA, 0 } };
// This parser assumes well formed input. You have to add all the error
// checking yourself.
size_t i = 0;
while (i < sizeof(tokStream)/sizeof(struct token)) {
if (tokStream[i+1].type == COMMA) {
print(tokStream[i].value, tokStream[i].value);
i += 2; // skip to next number
}
else { // DASH
print(tokStream[i].value, tokStream[i+2].value);
i += 4; // skip to next number
}
}
return 0;
}