Converting a string to roman numerals in C++ [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to convert integer value to Roman numeral string?
first time asking a question here. I have a project on the horizon I've been working a bit on, and can't seem to find anything like it asked here or elsewhere. The goal is to accept an integer with no upper constraint, and convert that into a roman numeral. Since integers do in fact have an upper boundary, I have to convert it into a string before parsing it, using apostrophes to denote each set of three characters placed into a sub-string. I'm having trouble conceptualizing the loops that a)assign roman numerals to what they see based on their location b)count each set of three by displaying apostrophes.
So far I have:
for(int i=0;i<(int)input.length()/3;i++){
temp=input.substr(i,3);
for(int j = 0; j < (int)temp.length(); j++){
if(j == 0){
if(temp[j] == '9') cout<<"CM";
else if(temp[j] >= '8') cout<<"DCCC";
else if(temp[j] >= '7') cout<<"DCC";
else if(temp[j] >= '6') cout<<"DC";
else if(temp[j] >= '5') cout<<"D";
else if(temp[j] == '4') cout<<"CD";
else if(temp[j] == '3') cout<<"CCC";
else if(temp[j] == '2') cout<<"CC";
else if(temp[j] == '1') cout<<"C";
}
else if(j == 1){
if(temp[j] == '9') cout<<"XC";
else if(temp[j] >= '8') cout<<"LXXX";
else if(temp[j] >= '7') cout<<"LXX";
else if(temp[j] >= '6') cout<<"LX";
else if(temp[j] >= '5') cout<<"L";
else if(temp[j] == '4') cout<<"XL";
else if(temp[j] == '3') cout<<"XXX";
else if(temp[j] == '2') cout<<"XX";
else if(temp[j] == '1') cout<<"X";
}
else if(j ==2){
if(temp[j] == '9') cout<<"IX";
else if(temp[j] == '8') cout<<"VIII";
else if(temp[j] == '7') cout<<"VII";
else if(temp[j] == '6') cout<<"VI";
else if(temp[j] >= '5') cout<<"V";
else if(temp[j] == '4') cout<<"IV";
else if(temp[j] == '3') cout<<"III";
else if(temp[j] == '2') cout<<"II";
else if(temp[j] == '1') cout<<"I";
}
}
}
The numerals display well enough on their own, but I'm having trouble figuring out how to tell the loop to start on the right, and work it's way left by threes, maintaining the actual place of the number in the input (e.g. 1234 should display 1 as I, not C. I also need to figure out the loop to write in the apostrophes.

Simplest way i can think of to convert to roman numerals, is to check starting from the largest possible digit/combo and work down. Include combos, and check them from largest to smallest, so that say "XC" always gets checked before "L" and you don't have to worry about "LXXXX" and "LXL" and such.
// This code requires C++11 support. Namely, initializer lists and type inference.
// If your compiler sucks, there's equivalents for the important stuff. What really
// really matters is the order of the value=>digits mappings, and the iteration over
// them in the for loop.
#include <vector>
#include <string>
#include <utility>
std::string romanNumeralFor(int n, int markCount = 0) {
typedef std::pair<int, std::string> valueMapping;
static std::vector<valueMapping> importantNumbers = {
{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"},
{100, "C"}, { 90, "XC"}, { 50, "L"}, { 40, "XL"},
{10, "X"}, { 9, "IX"}, { 5, "V"}, { 4, "IV"},
{1, "I"},
};
std::string result;
bool needMark = false;
std::string marks(markCount, '\'');
for (auto mapping : importantNumbers) {
int value = mapping.first;
std::string &digits = mapping.second;
while (n >= value) {
result += digits;
n -= value;
needMark = true;
}
if ((value == 1000 || value == 100 || value == 10 || value == 1) && needMark) {
result += marks;
needMark = false;
}
}
return result;
}
As for converting a string to a number:
// in C++11
int n = std::stoi(str);
// in C++03
std::istringstream iss(str);
int n;
iss >> n;
So, split your string up into three-digit chunks (starting from the end!), and pass them in with appropriate mark counts.

Not the best solution but it works.
#include <iostream>
#include <map>
#include <algorithm>
#include <string>
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
int romanNumeralToInt(std::string s) {
std::map<char,int> romanNum = {
{'I',1}, {'V',5}, {'X',10}, {'L',50}, {'C',100},
{'D',500}, {'M',1000}
};
//{"IIIII",5}, {"VV",10}, {"XXXXX",50}, {"LL",100}, {"CCCCC",500}, {"DD",1000}
int g = 0;
std::sort(s.begin(),s.end());
if(s.find("IIIII") != std::string::npos)
replaceAll(s,"IIIII","V");
if(s.find("VV") != std::string::npos)
replaceAll(s,"VV","X");
if(s.find("XXXXX") != std::string::npos)
replaceAll(s,"XXXXX","L");
if(s.find("LL") != std::string::npos)
replaceAll(s,"LL","C");
for(auto& i : s) {
if(romanNum[i] != 0)
g += romanNum[i];
}
return g;
}
int main() {
std::string st = "XXXXXIIIIIVVVXLLD";
int a = romanNumeralToInt(st);
std::cout << a;
}
Prints out 680.

Related

Why is this c++ code producing weird characters?

I wanted to simplify algebraic expressions in c++. I started with something simple: removing brackets from a algebraic expression containing + and - operators. For example, a-(b-c) should be simplified to a-b+c. This link gives the answer to this question. Here is the source code:
#include <iostream>
#include <string.h>
#include <stack>
using namespace std;
// Function to simplify the string
char* simplify(string str)
{
int len = str.length();
// resultant string of max length equal
// to length of input string
char* res = new char(len);
int index = 0, i = 0;
// create empty stack
stack<int> s;
s.push(0);
while (i < len) {
if (str[i] == '+') {
// If top is 1, flip the operator
if (s.top() == 1)
res[index++] = '-';
// If top is 0, append the same operator
if (s.top() == 0)
res[index++] = '+';
} else if (str[i] == '-') {
if (s.top() == 1)
res[index++] = '+';
else if (s.top() == 0)
res[index++] = '-';
} else if (str[i] == '(' && i > 0) {
if (str[i - 1] == '-') {
// x is opposite to the top of stack
int x = (s.top() == 1) ? 0 : 1;
s.push(x);
}
// push value equal to top of the stack
else if (str[i - 1] == '+')
s.push(s.top());
}
// If closing parentheses pop the stack once
else if (str[i] == ')')
s.pop();
// copy the character to the result
else
res[index++] = str[i];
i++;
}
return res;
}
I tested the function on a-(b-c-(d+e))-f and it gave the result a-b+c+d+e-fÿï┌. Why is it producing weird characters?

My 2D maze solver recurses infinitely and I get a stack overflow - why?

The Problem :-
I am trying to solve a 2d maze navigation problem in C++ using 2-dimensional array. To give a concise idea about the problem itself, I intend to navigate from node 'S' in the array to node 'G' by walking through free spaces denoted by '.' The nodes '#' are obstacles. One is not allowed to move on spaces denoted as obstacles. Care must also be taken to make all moves as legal moves (within configuration space). I denote the valid move with a '+' after replacement of the '.' If you like to know more about this problem (not necessary) then please refer this link.
What is the issue ?
I coded a recursive algorithm for this problem where we receive an array and a start node position, and then try to navigate to the goal node using recursion. However, I am getting a stack overflow error. It seems like my recursion never stops. I strongly believe there is some problem in my play() function or my check() function. I am not sure what actually is the problem.
What did I try ?
I am reproducing my code below :
void spawn(std::string (&board)[6]) {
for (int i = 0; i <= 6; i++) {
std::cout << board[i] << std::endl;
}
}
bool check(size_t a, size_t b, const std::string (&board)[6]) {
if (a < board[1].size() && a >= 0 && b < board[1].size() && b >= 0) {
if (board[a][b] == '#' || board[a][b] == '+')
return false;
else if (board[a][b] == '.')
return true;
}
return false;
}
void play(std::string (&board)[6], size_t a, size_t b) {
auto status = check(a, b, board);
if (board[a][b] == 'G' || board[a][b] == 'g') {
spawn(board);
return;
}
if (status) {
board[a][b] = '+';
play(board, ++a, b);
play(board, --a, b);
play(board, a, ++b);
play(board, a, --b);
}
}
int main() {
std::string grid[6] = {{"S#####"},
{".....#"},
{"#.####"},
{"#.####"},
{"...#.G"},
{"##...#"}};
play(grid, 0, 0);
return 0;
}
The check function prevents recursion because it sees the 'S' in the grid at the starting location. Changing:
else if (board[a][b] == '.')
to
else if (board[a][b] == '.' || board[a][b] == 'S')
got it to work for me.
Thanks for the insights Perette and Retired Ninja. I refactored the play() and check() functions in light of your suggestions and also some more ideas by myself.
I figured out that the main issue with the segmentation fault error was not providing accommodation for the '\0' character at the end of array of strings grid. I overlooked it because I considered array of strings to function in a different way than array of chars (since they are not the same species). Now I realize that the '\0' is necessary even for an array of strings !
I am reproducing the refactored functions for the purpose of completeness of this post :
void spawn(std::string board[6]) {
for (int i = 0; i <= 6; i++) {
std::cout << board[i] << std::endl;
}
}
bool check(int a, int b, const std::string board[6]) {
if (a < board[1].size() && a >= 0 && b <
board[1].size() && b >= 0) {
if (board[a][b] == '#' || board[a][b] == '+') {
return false;
} else if (board[a][b] == '.' ||
board[a][b] == 'S' ||
board[a][b] == 'G') {
return true;
}
}
return false;
}
void play(std::string board[6], int a, int b) {
if (board[a][b] == 'G' || board[a][b] == 'g') {
board[0][0] = 'S';
spawn(board);
return;
}
if (board[a][b] == '.' || board[a][b] == 'S')
board[a][b] = '+';
if (check(a + 1, b, board)) play(board, a + 1, b);
if (check(a - 1, b, board)) play(board, a - 1, b);
if (check(a, b + 1, board)) play(board, a, b + 1);
if (check(a, b - 1, board)) play(board, a, b - 1);
if (board[a][b] == '+') board[a][b] = '.';
}
int main() {
std::string grid[7] = {{"S#####"},
{".....#"},
{"#.####"},
{"#.####"},
{"...#.G"},
{"##...#"}};
play(grid, 0, 0);
return 0;
}

Conversion between numerical bases going wrong

I don't have a clue on what is wrong with my program. I just went testing if this would be usable on at least a console application before adding this "experiment"to my project and as you can see I failed.
I made a library that converts a number between numerical bases (so far, only B16 or B2 to decimal). It's a big work-around since I couldn't understand very well how to manage modulus and divisions, but it seemingly worked until a point.
I gave it a 0xFF and it treated it as 0xFF0, the result being 4,080. I gave it a 0x16 and it treated it like a 0x160, the result being 352. It's obvious what is happening, but I don't know why it is. At least, so far, the problem is only being with B16, although B2 did gave me some weird errors as well.
Here is the code (Code::Blocks on GNU compiler):
int bin_to_int(char char_digit){
char_digit = toupper(char_digit);
if (char_digit == '0') {return 0;}
else if (char_digit == '1') {return 1;}
else {return 2;}
}
int hex_to_int(char char_digit){
char_digit = toupper(char_digit);
if (char_digit == '0') {return 0;}
else if (char_digit == '1') {return 1;}
else if (char_digit == '2') {return 2;}
else if (char_digit == '3') {return 3;}
else if (char_digit == '4') {return 4;}
else if (char_digit == '5') {return 5;}
else if (char_digit == '6') {return 6;}
else if (char_digit == '7') {return 7;}
else if (char_digit == '8') {return 8;}
else if (char_digit == '9') {return 9;}
else if (char_digit == 'A') {return 10;}
else if (char_digit == 'B') {return 11;}
else if (char_digit == 'C') {return 12;}
else if (char_digit == 'D') {return 13;}
else if (char_digit == 'E') {return 14;}
else if (char_digit == 'F') {return 15;}
else {return 16;}
}
void conv_todec(int mode_base){
if (mode_base == 16){std::cout << "\nHex to Dec function";}
if (mode_base == 2){std::cout << "\nBin to Dec function";}
if (mode_base == 16){std::cout << "\nEnter a hex number: ";}
if (mode_base == 2){std::cout << "\nEnter a bin number: ";}
std::string numb;
std::cin >> numb;
int str_leg = numb.length();
int total = 0;
for(int i(0);i<str_leg;i++){
if (mode_base == 16){
if (hex_to_int(numb[i]) == 16){func_erro();return;}}
if (mode_base == 2){
if (bin_to_int(numb[i]) == 2){func_erro();return;}}
}
int digits[str_leg] = {};
int j = str_leg;
for(int i(0);i<str_leg;i++){
if (mode_base == 16) {digits[i] = hex_to_int(numb.at(i));}
if (mode_base == 2) {digits[i] = bin_to_int(numb.at(i));}
if (mode_base == 16) {total += (digits[i] * power(mode_base,j--));}
//Somehow the number never had the first digit claiming it as
//either even or odd, so I tried pre-decrementing 'j' and it
//worked for some reason I don't know either.
if (mode_base == 2) {total += (digits[i] * power(mode_base,--j));}}
std::cout << "The result is: " << total << std::endl;
}
This line:
int j = str_leg;
means that for hex FF, j will be 2. This means that later on when you do power(mode_base,j--) to multiply with the first digit, you will be doing power(16, 2) (remember that j-- means take the value of j, then subtract one from j, but still use the original value). So you will be multiplying the first digit by 256, where you only wanted to be multiplying by 16.
The solution? Start j as one smaller.
int j = str_leg - 1;
Note, this is why your binary version works when you use --j as you are decrementing j before you use it. So either use --j and j = str_leg, or j-- and j = strleg - 1.
Also, work on the modulo parts - it will make your program a lot easier to write.

C++ Encoding Error

The object of this code is to take the command line argument ./Program -encode|decode 0-9 and apply it to a text string written on the next line. IF encode, then shift each character of the text however many places specified in the alphabet. Decode does the opposite. I've gotten it good enough that the compiler finds nothing wrong, but when running the program nothing returns after entering the text string.
#include <iostream>
#include <string>
#include <cctype>
#include <cstdlib>
#include <cstring>
using namespace std;
string encode(string& to_encode, int to_shift)
{for(int i = 0; I < to_encode.length(); i++)
{if(isalpha(to_encode[i]))
{char sum = to_encode[i] + to_shift;
if(isupper(to_encode[i]) != 0 && isupper(sum) == 0)
{to_encode[i] = sum - 'Z' + 'A';
}
if(isupper(to_encode[i]) != 0 && isupper(sum) != 0)
{to_encode[i] = sum;
}
if(islower(to_encode[i]) != 0 && islower(sum) == 0)
{to_encode[i] = sum - 'z' + 'a';
}
if(islower(to_encode[i]) != 0 && islower(sum) != 0)
{to_encode[i] = sum;
}
}
}
return to_encode;
}
string decode(string& to_decode, int to_shift)
{for(int i = 0; I < to_decode.length(); i++)
{if(isalpha(to_decode[i]))
{char difference = to_decode[i] + to_shift;
if(isupper(to_decode[i]) != 0 && isupper(difference) == 0)
{to_encode[i] = difference + 'Z' - 'A';
}
if(isupper(to_decode[i]) != 0 && isupper(difference) != 0)
{to_encode[i] = difference;
}
if(islower(to_decode[i]) != 0 && islower(difference) == 0)
{to_encode[i] = difference + 'z' - 'a';
}
if(islower(to_decode[i]) != 0 && islower(difference) != 0)
{to_encode[i] = difference;
}
}
}
return to_decode;
}
int main(int argc, char* argv[])
{string cryption;
in shift;
if(argc != 3)
{cerr<<"Usage: ./Prog1d -encode|decode 0-9"<<endl;
return -1;
}
if(!(strcmp(argv[1],"-encode") == 0 || strcmp(argv[1],"-decode") == 0)
{cerr<<"Usage: ./Prog1d -encode|decode 0-9"<<endl;
return -1;
}
shift = atoi(argv[2]);
if(shift > 0 && shift <= 9)
{while(getline(cin,cryption))
{if(argv[1] == "-encode")
{encode(cryption, shift);
}
else
{decode(cryption, shift);
}
}
}
else
{cerr<<"Usage: ./Prog1d -encode|decode 0-9"<<endl;
return -1;
}
}

C++ function not found during compilation

For a homework assignment: I'm supposed to create randomized alphabetial keys, print them to a file, and then hash each of them into a hash table using the function "goodHash", found in my below code.
When I try to run the below code, it says my "goodHash" "identifier isn't found". What's wrong with my code?
#include <iostream>
#include <vector>
#include <cstdlib>
#include "math.h"
#include <fstream>
#include <time.h>
using namespace std;
// "makeKey" function to create an alphabetical key
// based on 8 randomized numbers 0 - 25.
string makeKey() {
int k;
string key = "";
for (k = 0; k < 8; k++) {
int keyNumber = (rand() % 25);
if (keyNumber == 0)
key.append("A");
if (keyNumber == 1)
key.append("B");
if (keyNumber == 2)
key.append("C");
if (keyNumber == 3)
key.append("D");
if (keyNumber == 4)
key.append("E");
if (keyNumber == 5)
key.append("F");
if (keyNumber == 6)
key.append("G");
if (keyNumber == 7)
key.append("H");
if (keyNumber == 8)
key.append("I");
if (keyNumber == 9)
key.append("J");
if (keyNumber == 10)
key.append("K");
if (keyNumber == 11)
key.append("L");
if (keyNumber == 12)
key.append("M");
if (keyNumber == 13)
key.append("N");
if (keyNumber == 14)
key.append("O");
if (keyNumber == 15)
key.append("P");
if (keyNumber == 16)
key.append("Q");
if (keyNumber == 17)
key.append("R");
if (keyNumber == 18)
key.append("S");
if (keyNumber == 19)
key.append("T");
if (keyNumber == 20)
key.append("U");
if (keyNumber == 21)
key.append("V");
if (keyNumber == 22)
key.append("W");
if (keyNumber == 23)
key.append("X");
if (keyNumber == 24)
key.append("Y");
if (keyNumber == 25)
key.append("Z");
}
return key;
}
// "makeFile" function to produce the desired text file.
// Note this only works as intended if you include the ".txt" extension,
// and that a file of the same name doesn't already exist.
void makeFile(string fileName, int n) {
ofstream ourFile;
ourFile.open(fileName);
int k; // For use in below loop to compare with n.
int l; // For use in the loop inside the below loop.
string keyToPassTogoodHash = "";
for (k = 1; k <= n; k++) {
for (l = 0; l < 8; l++) { // For-loop to write to the file ONE key
ourFile << makeKey()[l];
keyToPassTogoodHash += (makeKey()[l]);
}
ourFile << " " << k << "\n";// Writes two spaces and the data value
goodHash(keyToPassTogoodHash); // I think this has to do with the problem
makeKey(); // Call again to make a new key.
}
}
// Primary function to create our desired file!
void mainFunction(string fileName, int n) {
makeKey();
makeFile(fileName, n);
}
// Hash Table for Part 2
struct Node {
int key;
string value;
Node* next;
};
const int hashTableSize = 10;
Node* hashTable[hashTableSize];
// "goodHash" function for Part 2
void goodHash(string key) {
int x = 0;
int y;
int keyConvertedToNumber = 0;
// For-loop to produce a numeric value based on the alphabetic key,
// which is then hashed into hashTable using the hash function
// declared below the loop (hashFunction).
for (y = 0; y < 8; y++) {
if (key[y] == 'A' || 'B' || 'C')
x = 0;
if (key[y] == 'D' || 'E' || 'F')
x = 1;
if (key[y] == 'G' || 'H' || 'I')
x = 2;
if (key[y] == 'J' || 'K' || 'L')
x = 3;
if (key[y] == 'M' || 'N' || 'O')
x = 4;
if (key[y] == 'P' || 'Q' || 'R')
x = 5;
if (key[y] == 'S' || 'T')
x = 6;
if (key[y] == 'U' || 'V')
x = 7;
if (key[y] == 'W' || 'X')
x = 8;
if (key[y] == 'Y' || 'Z')
x = 9;
keyConvertedToNumber = x + keyConvertedToNumber;
}
int hashFunction = keyConvertedToNumber % hashTableSize;
Node *temp;
temp = new Node;
temp->value = key;
temp->next = hashTable[hashFunction];
hashTable[hashFunction] = temp;
}
// First two lines are for Part 1, to call the functions key to Part 1.
int main() {
srand ( time(NULL) ); // To make sure our randomization works.
mainFunction("sandwich.txt", 5); // To test program
cin.get();
return 0;
}
I realize my code is cumbersome in some sections, but I'm a noob at C++ and don't know much to do it better.
I'm guessing another way I could do it is to AFTER writing the alphabetical keys to the file, read them from the file and hash each key as I do that, but I wouldn't know how to go about coding that.
C++ expected everything to be declared in order, so that nothing's used before it's declared.
If you need to refer to a function higher in the file than where it's defined, you need to have a function prototype near the top of the file that declares the function. (Writing prototypes for all functions is a standard practice as a result of this.)
Near the top of the file (after the #includes) simply add
void goodHash(string key);
Definitions
Function declaration: something that declares the name of the function and the types the function takes.
Function definition: something that specifies the actual code of the function.
if you insert
void goodHash(string key);
in the line under "using namespace..." it will work
The issue is that you have to forward declare goodHash or define goodHash before makeFile if you want to use goodHash in makeFile. Otherwise, when the compile is in makeFile, it sees the token goodHash and hasn't found out what it means, which is why you are getting the compile-time error.
EDIT: Here is a good resource on forward declarations
you forgot the function prototype just add this in the top:
void goodHash(string key);
and btw your makeKey() is too long
you can try this instead:
string makeKey() {
int k;
string key = "";
for (k = 0; k < 8; k++) {
int keyNumber = (rand() % 25);
char app[2];
app[0] = keyNumber + 'A';
app[1] = 0;
key.append(app);
}
return key;
}