I'm meant to write a function that keeps the first and last letter of a string the same and ignores any non-letters after the last letter. I'm supposed to use the std::random_shuffle(). I read the documentation however I don't seem to grasp the concept of this function. This is my code:
#include <iostream>
#include <algorithm>
#include <string>
std::string mix(std::string s){
int last_char;
if(std::isalpha(s[s.size()-1]) == true){
last_char = s.size()-1;
} else {
for(int i=s.size()-1 ; i>0; --i){
if((std::isalpha(s[i]) == false) && (std::isalpha(s[i-1])==true)){
last_char = i -1;
break;
}
}
}
std::random_shuffle(&s[1],&s[last_char]);
return s;
}
int main(){
std::string test = "Hello";
std::cout << mix(test) << std::endl;
}
edit: Now however I keep getting the error: segmentation fault(core dumped). ANyone got an idea why? Can't seem to find the problem.
std::random_shuffle takes iterators or pointers, as arguments, not values in the array/container to be sorted. Your call to std::random_shuffle should probably be:
std::random_shuffle(&s[1],&s[last_char]);
Note that the second parameter is the ending iterator value. The ending iterator doesn't point to the last value to sort, but the one after that.
This is not the only problem with the shown code. You'll need to fix several bugs in your code that precedes the call to std::random_shuffle. For example:
for(int i=s.size() ; i>0; --i){
if((std::isalpha(s[i]) == false) && (std::isalpha(s[i-1])==true)){
s.size() gives you the size of the string. On the first iteration, i will be equal to its size(), but accessing s[i] will now result in undefined behavior, and a bug, since s[i] obviously does not exist. In a string that contains n characters, the characters are s[0] through s[n-1], of course.
You will need to fix your algorithm so that last_char ends up being the index of the next character after the one you want to shuffle, and then use the fixed std::random_shuffle call, above.
Or, alternatively, compute last_char to be the index of the last character to sort, and call
std::random_shuffle(&s[1],&s[last_char+1]);
Either approach will be fine.
You'll need to find the leftmost "non-letter" in the right side of your string.
One place to the left of this is the position of the last letter.
One place to the right is your first letter.
Simply invoke random_shuffle with your "first" and "last".
Here are some useful links:
http://www.cplusplus.com/reference/algorithm/random_shuffle/
Remember that "begin" is inclusive, "end" is exclusive"
Something to get you started. It has at least one corner case that you'll have to fix. Visit cppreference.com to understand how the algorithms work.
#include <iostream>
#include <cctype>
#include <algorithm>
#include <string>
std::string
special_shuffle(std::string s)
{
if (s.size() < 3) return s;
auto begin = std::find_if(s.begin(), s.end(), ::isalpha);
auto end = std::find_if(s.rbegin(), s.rend(), ::isalpha).base();
std::random_shuffle(++begin, --end);
return s;
}
int
main()
{
std::string s1 = "Hello World!";
std::string s2 = "AB";
std::string s3 = "A";
std::string s4 = "";
std::string s5 = "a string going from a to z";
std::cout << s1 << " --> " << special_shuffle(s1) << "\n"
<< s2 << " --> " << special_shuffle(s2) << "\n"
<< s3 << " --> " << special_shuffle(s3) << "\n"
<< s4 << " --> " << special_shuffle(s4) << "\n"
<< s5 << " --> " << special_shuffle(s5) << "\n";
}
Compile and run:
$ g++ example.cpp -std=c++14 -Wall -Wextra
$ ./a.out
Hello World! --> Hooll eWlrd!
AB --> AB
A --> A
-->
a string going from a to z --> aarfritomgi nnso t g goz
Related
I am writing a program that uses a substitution cipher.
I am trying to replace each char in a string (that the user entered) with a char from another string (the encryption key string).
But I am having a bunch of issues doing this with the string.replace() function. It does not replace the the char in the userMessage with the correct char. Although this is not an issue when using a string literal as initialization value for letterReplacement. In addition to this, it somehow temporarily increases the size of the userMessage resulting in the loop running for e.g 5 times in a message 3 char long (no matter how letterReplacement was initialized). I would appreciate any information on why this happens.
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string userMessage;
std::cout << "\nWelcome to my Military Grade* Encryption Software!" << std::endl;
std::cout << "--------------------------------------------------" << std::endl << std::endl;
std::cout << "Please enter a secret message you would like to encrypt:\n-" << std::endl;
getline(std::cin, userMessage);
std::cout << "-" << std::endl << std::endl;
const std::vector <std::string> encryptionKey {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZT.,<>!?+- '",
"ß?pl ,;0=}okm9)]ijN8([uhb7/{zgv6&ctf%xdrys4$e3w|<2Q>1!+#ä-:*GnB"};
for(size_t i {0}; i < userMessage.size(); ++i) // Here we encrypt the Message
{
char currentCharInUM {userMessage.at(i)};
size_t currentKeyPosition {encryptionKey.at(0).find(currentCharInUM)}; //finds the position that currentChar is at in first encryptionKey String
std::cout << "currentKeyPosition: " << currentKeyPosition << " - " << i << std::endl;
if(currentKeyPosition == std::string::npos) // is letter in userMessage is not in encryptionKey just keep it (skip iteration)
continue;
std::string letterReplacement {encryptionKey.at(1).at(currentKeyPosition)};
userMessage.replace(i,1,letterReplacement);
}
std::cout << userMessage;
return 0;
}
The first character of 2nd element of the vector encryptionKey 'ß' is equal to 2 bytes. So that when you enter 'a' it returns '├' and when you enter b it returns 'ş'. You can try to put another character instead of 'ß'.
If your intention was indeed to store non-ASCII characters, it would have been more advantageous to use a lookup table.
The table could be a std::unordered_map<char, wchar_t> to map the English letters to the encrypted characters:
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
// Strings
const char encryptionKeyA[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZT.,<>!?+- '";
const wchar_t encryptionKeyW[] = L"ß?pl ,;0=}okm9)]ijN8([uhb7/{zgv6&ctf%xdrys4$e3w|<2Q>1!+#ä-:*GnB";
// Create the lookup table bassed on the above strings
std::unordered_map<char, wchar_t> encryptionMap;
for (int i = 0; i < sizeof(encryptionKeyA); ++i)
encryptionMap[encryptionKeyA[i]] = encryptionKeyW[i];
// Test
std::string userMessage = "abcTest";
// The output string
std::wstring encryptedString;
// Now go through each character of the userMessage
for(auto ch : userMessage)
{
// If the character exists, append the associated encrypted character
auto iter = encryptionMap.find(ch);
if (iter != encryptionMap.end())
encryptedString += iter->second;
else
encryptedString += L' '; // Just append a space if it didn't exist
}
std::wcout << encryptedString;
}
Output:
ss?p1 N8
Note that the output is what is displayed if the locale is English. If the terminal/output were set to German, I would assume that the output would be:
ß?p1 N8
since the first character is the "double-s".
Also note that I am assuming that both the original and encrypted character strings are of the same size (in terms of the number of characters) when building the map. If they're not the same size, then the loop to build the map should be adjusted accordingly.
I'm trying to reverse a string in my C++ code line below revStr.at(j) = str.at(size);
But it doesn't change any of the elements in revStr.
Is there another way to do it without using any libraries.
#include <iostream>
#include<sstream>
#include <iterator>
using namespace std;
int main() {
ostringstream d;
long long c = 123456789;
d << c;
//cout << c << endl;
string str = d.str();
//cout << str.at(0) << endl;
int size = str.size() - 1;
//cout << size << endl;
ostringstream e;
e << str;
string revStr = e.str();
for (int i = size; size==0; size--) {
//cout << str.at(size);
int j = 0;
revStr.at(j) = str.at(size);
j++;
} // End For
cout << "Original String is :" << str << endl;
cout << "Reversed String is :" << revStr << endl;
}
Use std::reverse:
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
std::string test{"Hello"};
std::cout << "Original string: " << test << std::endl;
std::reverse(test.begin(), test.end());
std::cout << "Reversed string: " << test << std::endl;
return 0;
}
Output:
Original string: Hello
Reversed string: olleH
If you just want to reverse a string, you should use std::reverse, as described by Tyler Lewis. It is the best option.
If you want to learn C++, then writing your own version is good practice.
The line
for (int i = size; size==0; size--)
means “Create a new int called i and set it to size initially. Then, while size is zero, do the following and then decrement size”.
There are three problems with this:
Size is not zero unless you entered a one-character string
Since you never use i, there’s no point in declaring it
Inside the loop you use j which is set to zero each time.
You can fix the first by changing the middle part of the for loop to size >= 0 (but be careful—if you later change it so that size is an unsigned type, because it doesn’t make sense for it to be negative, that code won’t work; it’s generally better to increment going up instead). You can fix the second by using i everywhere in the loop statement, and not changing size. You can fix the third by using i in the loop body, and not declaring a new variable inside the loop.
I noticed you used std::string so I used std function swap and string. Depending on if you consider this as a 'library'. There are several definitions of 'reverse'. You could reverse the word order in a string, or a pure char to char reversal like I wrote. Reversal could also mean changing character case, etc... but this is simply swap first and last. Then swap the 2nd and 2nd to last, then swap the 3rd and 3rd to last, etc...
So some points from your code. You only need to loop half the string length. The swap is from the ith and the ith to last. So the last is numCharacters - 1, thus the ith to last would be Last - i or numCharacters - 1 - i. I believe this is what you intended by using a farLeft(i) and a farRight(j) index.
#include <iostream>
void reverseStringInPlace(std::string &stringToReverse)
{
int numCharacters = stringToReverse.length();
for (int i=0; i<numCharacters/2; i++)
{ std::swap(stringToReverse[i], stringToReverse[numCharacters-i-1]); }
}
int main()
{
std::string stringToReverse = "reversing a string";
std::cout << stringToReverse << std::endl;
reverseStringInPlace(stringToReverse);
std::cout << stringToReverse << std::endl;
return 0;
}
Output:
reversing a string
gnirts a gnisrever
Changes made to the piece of code in question, it works.
for (unsigned int i = size; size >= 0; size--) {
revStr[j] = str[size];
j++;
}
I am trying to write a code that will search userInput for the word "darn" and if it is found, print out "Censored". if it is not found, it will just print out the userInput. It works in some cases, but not others. If the userInput is "That darn cat!", it will print out "Censored". However, if the userInput is "Dang, that was scary!", it also prints out "Censored". I am trying to use find() to search for the string literal "darn " (the space is because it should be able to determine between the word "darn" and words like "darning". I am not worrying about punctuation after "darn"). However, it seems as though find() is not doing what I would like. Is there another way I could search for a string literal? I tried using substr() but I couldn't figure out what the index and the len should be.
#include <iostream>
#include <string>
using namespace std;
int main() {
string userInput;
userInput = "That darn cat.";
if (userInput.find("darn ") > 0){
cout << "Censored" << endl;
}
else {
cout << userInput << endl;
} //userText.substr(0, 7)
return 0;
}
The problem here is your condition. std::string::find returns a object of std::string::size_type which is an unsigned integer type. That means it can never be less than 0 which means
if (userInput.find("darn ") > 0)
will always be true unless userInput starts with "darn ". Because of this if find doesn't find anything then it returns std::string::npos. What you need to do is compare against that like
if (userInput.find("darn ") != std::string::npos)
Do note that userInput.find("darn ") will not work in all cases. If userInput is just "darn" or "Darn" then it won't match. The space needs to be handled as a separate element. For example:
std::string::size_type position = userInput.find("darn");
if (position != std::string::npos) {
// now you can check which character is at userInput[position + 4]
}
std::search and std::string::replace were made for this:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string userInput;
userInput = "That darn cat is a varmint.";
static const string bad_words [] = {
"darn",
"varmint"
};
for(auto&& bad : bad_words)
{
const auto size = distance(bad.begin(), bad.end());
auto i = userInput.begin();
while ((i = std::search(i, userInput.end(), bad.begin(), bad.end())) != userInput.end())
{
// modify this part to allow more or fewer leading letters from the offending words
constexpr std::size_t leading_letters = 1;
// never allow the whole word to appear - hide at least the last letter
auto leading = std::min(leading_letters, std::size_t(size - 1));
auto replacement = std::string(i, i + leading) + std::string(size - leading, '*');
userInput.replace(i, i + size, replacement.begin(), replacement.end());
i += size;
}
}
cout << userInput << endl;
return 0;
}
expected output:
That d*** cat is a v******.
I'm a beginner at c++(took a couple classes, then no c++ for a while, then starting back up several months later), and I'm trying to count the number of words in a simple sentence and then count the number of numbers in that same sentence. To count the words, I use:
int countWord(char *word)
{
int counter = 0;
char *words = strtok(word, " 0123456789-");
while (words != NULL)
{
counter++;
words = strtok(NULL, " 0123456789-");
}
return counter;
}
The number counter is basically the same, just instead of using integers I use the alphabet.
char *num = strtok(number, " abcdefghijklmnopqrstuvwxyz");
My main is:
int main()
{
char str[] = "what time is 88 it 99today";
cout << "words = " << countWord(str) << " " << "numbers = " <<
countNum(str) << endl;
system("pause");
return 0;
}
When I run this it outputs: words = 3 numbers = 2.
When i rearrange main to:
char str[] = "what time is 88 it 99today";
cout << "words = " << countWord(str) << " ";
cout << "numbers = " << countNum(str) << endl;
output is: words = 5 numbers = 0
Can anyone explain why this is incorrect? Also, if anyone can refer me to a text that covers this, I'd appreciate that. The text I learned from is: "C++ Programming: Program Design Including Data Structures by D.S. Malik. I didn't see any techniques in this book to count "words". Thank you.
The issue is that strtok marks the end of tokens in the original string by a null character. Citing from cppreference:
If such character was found, it is replaced by the null character '\0' and the pointer to the following character is stored in a static location for subsequent invocations.
Notes: This function is destructive: it writes the '\0' characters in the elements of the string str. In particular, a string literal cannot be used as the first argument of strtok.
In your case the line
cout << "words = " << countWord(str) << " " << "numbers = " <<
countNum(str) << endl;
is a composition of operator<<, like
...operator<<(operator<<(cout, "words"), countWord(str))...
so the line countNum(str) is evaluated first. Then countWord(str) is evaluated secondly. This is in contrast to
cout << "words = " << countWord(str) << " ";
cout << "numbers = " << countNum(str) << endl;
where the other way around happens.
One solution is to use a copy of the original string when using strtok, e.g. use strtok(strdup(str)) every time. Better yet, use standard C++ library features, like std::string, std::count_if etc. I'm sure there are plenty of word counting solutions around using pure C++.
Vlad has submitted a nice answer for your C-style code. My answer is demonstrating use of more C++ libraries to help move things along:
#include <iostream>
#include <string>
#include <vector>
#include <regex>
int main() {
// The main string.
std::string str = "what time is 88 it 99today";
// Space is your delimiter
std::string delimiter = " ";
// Create a regex string for matching numbers, including floating point.
std::regex number_chars(std::string("[0123456789.]+"));
// A lambda function to help tokenize strings.
// Returns a vector of substring tokens.
// The internal code is taken from an answer on Stack Overflow.
auto tokenizer = [](std::string s, std::string delimiter) {
size_t pos = 0;
std::string token;
std::vector<std::string> tokens;
while (pos = (s.find(delimiter))) {
token = s.substr(0, pos);
tokens.push_back(token);
s.erase(0, pos + delimiter.length());
if (pos == std::string::npos)
break;
}
return tokens;
};
// Apply the lambda.
auto tokens = tokenizer(str, delimiter);
// Output your tokens.
for (auto it : tokens) {
std::cout << it << "\n";
} std::cout << "\n";
// Output tokens that are numbers.
for (auto it : tokens) {
if (std::regex_match(it, number_chars)) {
std::cout << "String: " << it << " is a number.\n";
}
}
return 0;
}
Since C++ has a regular expression library in C++11, it would be good to leverage it.
Coliru: http://coliru.stacked-crooked.com/a/43cd6711e1243f4a
My homework is remove duplicates in a random string. My idea is use 2 loops to solve the problem.
1st one will scan every character in the string.
2nd one will check that character is duplicated or not. If so, remove the character.
string content = "Blah blah..."
for (int i = 0; i < content.size(); ++i) {
char check = content.at(i);
for (int j = i + 1; j < content.size() - 1; ++j) {
if (check == content.at(j)) {
content.erase(content.begin()+j);
}
}
}
The problem is it doesn't work. It always removes the wrong character. Seems an indices problem but I don't understand why.
A temporary fix is change content.erase(content.begin()+j); to content.erase( remove(content.begin() + i+1, content.end(), check),content.end());
But I think trigger a "remove by value" scan isn't a nice way. I want to do it with 2 loops or fewer.
Any ideas will be appreciated :)
Your loops could look the following way
#include <iostream>
#include <string>
int main()
{
std::string s = "Blah blah...";
std::cout << '\"' << s << '\"' << std::endl;
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
std::string::size_type j = i + 1;
while ( j < s.size() )
{
if ( s[i] == s[j] )
{
s.erase( j, 1 );
}
else
{
++j;
}
}
}
std::cout << '\"' << s << '\"' << std::endl;
return 0;
}
The output is
"Blah blah..."
"Blah b."
There are many other approaches using standard algorithms. For example
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
int main()
{
std::string s = "Blah blah...";
std::cout << '\"' << s << '\"' << std::endl;
auto last = s.end();
for ( auto first = s.begin(); first != last; ++first )
{
last = std::remove( std::next( first ), last, *first );
}
s.erase( last, s.end() );
std::cout << '\"' << s << '\"' << std::endl;
return 0;
}
The output is the same as for the previous code example
"Blah blah..."
"Blah b."
If use of STL is a possible option, you could use an std::unordered_set to keep the characters seen so far and the erase-remove idiom with std::remove_if, like in the following example:
#include <iostream>
#include <string>
#include <unordered_set>
#include <algorithm>
int main() {
std::string str("Hello World!");
std::unordered_set<char> log;
std::cout << "Before: " << str << std::endl;
str.erase(std::remove_if(str.begin(), str.end(), [&] (char const c) { return !(log.insert(c).second); }), str.end());
std::cout << "After: " << str << std::endl;
}
LIVE DEMO
I recommend a two pass approach. The first pass identifies the positions of the duplicated characters; the second pass removes them.
I recommend using a std::set and a std::vector<unsigned int>. The vector contains letters that are in the string. The vector contains the positions of the duplicated letters.
The first pass detects if a letter is present in the set. If the letter exists, the position is appended to the vector. Otherwise the letter is inserted into the set.
For the second pass, sort the vector in descending order.
Erase the character at the position in the vector, then remove the position from the vector.
By erasing characters from the end of the string towards the front, the positions of the remaining duplicates won't change when the character is erased from the string.
I am not sure that this is what is causing your problem, but another problem that I see with your code is in your second for loop. Your j < content.size() - 1 statement should just be
j < content.size().
The reasoning for this is a little tricky to see at first, but in this case you are not just getting the size of your vector to act as the size, but to act as the ending indices of your string. You are shortening the last indices by one which means you wont hit the last char in your string. I don't know if this will help your initial problem, but who knows?
Note: Your actual problem is maintaining a proper index to the next element in question:
If you do not erase a character, the next element is at the next position.
If you erase a character, the next element will move into the place of the current position (the position stays the same).
Also: There are more efficient solutions (eg.: utilizing a set)