Add character at every vowel - c++

I had a similiar question earlier and managed to solve it. Now I'm trying to do it backwards, this is my code at the moment.
Basically I want the end result to "p5A5SSW5o5R5o5d", but when I run this it just find the first "o" which is in the first part of passworod, I need it to skip the the vowel it have already added a 5 before and after to.
I want every vowel (only included AOao in the current string as thats all the vowels appearing in my string), to have a 5 as prefix and suffix. It gets stuck at the first o and doesnt proceed to the next o. I have created a nested for loop which means it takes the first character in the encrypt-string, proceeds to the next for-loop and loops through every single vowel Ive included in the vowel string until it finds a match. Otherwise it restarts at the first for-loop but incremented by one. First go it should search the letter "p", second run it should search the letter "A" and so on.
Result: p5A5SSW55o55rod
Expected Result: p5A5SSW5o5R5o5d
In the end I will also want to rotate all the characters, but thats for another task, I think I can just use either if-statement or a switch to do that. If it ends up on a 5, do nothing, otherwise rotate.
I hope I made myself clear and provided you with all the relevant information, otherwise just holler in the comments.
Thanks in advance.
#include <string>
#include <sstream>
const string vowel = "AOao";
string encrypt, decrypt;
encrypt = "pASSWoRod";
decrypt = encrypt;
for (int i=0; i<encrypt.length(); i++){
for (int j=0; j<vowel.length(); j++){
if (encrypt[i] == vowel[j]){
decrypt.insert(decrypt.find(vowel[j]), 1, '5');
decrypt.insert(decrypt.find(vowel[j]) + 1, +1, '5');
}
}
}
return decrypt;
}

find, when not proved a starting point, always finds the first instance.
Searching and keeping track of the string length and where you've already inserted characters is much harder than it seems at first glance (as you've noticed).
Build the result from scratch instead of inserting characters into an initial string.
Also, implement this function (actual implementation left as an exercise):
bool is_vowel(char c);
and then
std::string encrypt = "pASSWoRod";
std::string decrypt;
for (auto c: encrypt)
{
if (is_vowel(c))
{
decrypt += '5';
decrypt += c;
decrypt += '5';
}
else
{
decrypt += c;
}
}

find with one argument starts from the beginning. You would need the other find.
However maintaing an index in decrypt, one no longer would need a find.
int encryptI = 0;
for (int i = 0; i < decrypt.length(); i++, encyptI++){
for (int j=0; j<vowel.length(); j++){
if (decrypt[i] == vowel[j]){
//encryptI = encrypt.find(vowel[j], encryptI);
encrypt.insert(encryptI, 1, '5');
encrypt.insert(encryptI + 1, +1, '5');
encryptI += 2;
break;
}
}
}
The code could be nicer.
string encrypt;
for (int i = 0; i < decrypt.length(); i++, decyptI++){
char ch = decrypt[i];
if (vowel.find(ch) != string::npos) {
encrypt.append('5');
encrypt.append(ch);
encrypt.append('5');
} else {
encrypt.append(ch);
}
}

Your find is stopping at the first instance of the vowel, 'o'. You should include a starting position for your find so you ignore the part of the decrypt string you've already analyzed.

Related

Problem with adding strings to a vector<set<string>>

Thank you for taking your time to read this! I have been practicing online and I came across this one problem which I am not able to solve.
Firstly, we enter a number and then enter some names. The number is the size of a vector<string> where we put the names. Afterwards, the elements of the said vector<string> are to be copied to a list<string>.
Afterwards, the program is supposed to copy the names to a vector<set<string>> where each set represents a team. The size of the teams is to be determined by a formula and I have no problems in calculating the sizes(for example, if the number of children is 10, and you wish to place them in three teams, then the sizes of the teams should be 4, 3, 3 respectively.)
Now here comes the tricky part for me. The copying is to be done as follows:
The first entered name is copied to the first set<string>. Afterwards, you take the number of letters in the name and iterate through the list<string> number of letters times. (If at one point you reach the end of the list, the iterator goes back to the beginning of the list)
The name you stop at is to be put into the current set until it is full. When you put the name in the corresponding set, it is then to be removed from the list.
I do not know how to do this. I get stuck right after I put the first name in the first set<string> and remove it from the list<string> I do not know how to return the iterator back at the beginning of the list if it reaches the end of the list and I do not know how to skip to the next set<string> if the current one is full.
Here is what I tried:
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <list>
using namespace std;
int LettersInWord(string s) { // a "letter" is an alphabetic character or a number
int counter = 0;
for(int i = 0; i < s.length(); i++) {
if((s[i] >= 65 && s[i] <= 90) || (s[i] >= 97 && s[i] <= 122) ||
(s[i] >= 48 && s[i] <= 57))
counter++;
}
return counter;
}
int main() {
vector<set<string>> vss(3); // three teams
list<string> names = {{"Name1"}, {"Name2"}, {"Name3"}, {"Name4"}, {"Name5"},
{"Name6"}, {"Name7"}, {"Name8"}, {"Name9"}, {"Name10"}};
vector<int> size_teams = {4, 3, 3};
auto it = names.begin();
string s = *it;
vss[0].insert(*it); // put the first name in the first set
it = names.erase(it); // erase it
int number_of_iterations = LettersInWord(s);
int counter_of_insertions = 0; // this counter keeps track of
// how many strings are inserted in a set, so
// as not to overflow the size
for(int i = 0; i < vss.size(); i++) { // iterate through the vector of sets
int counter_of_iterations = 0; // we compare this to counter_of_insertions
for(auto it = names.begin(); it != names.end(); it++) { // iterate through the list
if(it == names.end())
it = names.begin(); // if iterator is at the end of list, return it to
// beginning
counter_of_iterations = 0;
if(counter_of_iterations == number_of_iterations) {
vss[i].insert(*it); // insert word
counter_of_insertions++;
if(counter_of_insertions == size_teams[i]) i++;
counter_of_insertions = 0;
it = names.erase(it);
number_of_iterations = LettersInWord(*it); // get no of letters in next word
}
}
}
return 0;
}
What this does is it just copies the first name to the first set and does absolutely nothing else.
Whatever I try I simply cannot fix this. Would anyone be kind to make a modification to the code above? I am sorry for any bad phrasing, mistakes or errors.
Note: It is mandatory to use list<string> and <vector<set<string>>
Thanks to anyone who is willing to help in any way!
if(counter_of_iterations == number_of_iterations) {
This line can never be true.
It's easy to see in godbolt, because there's a huge chunk of code that doesn't get colored (meaning that it didn't have any machine code generated for it):
https://godbolt.org/z/_wm8ff
Also, line 39 can never run either, since that's the termination case for the for loop it's in.

member function erase() not working in a loop

I'm programming a little game; but stringname.erase() seems to be not working in a 'for-loop' , I want to understand why, I have other alternatives, but I don't understand what's going on in the following code.
More explications of my situation (Important!):
guess is a char.
'tmgword' and 'word' are of type string, and: tmgword = word ;
what I understand from my code:
in the first time,the 'while'-loop verifies if there is 'guess' in the string 'tmpgword'.
That is true and the for-loop is working fine, the right character(guess) that verifies the if-condition is erased.
in the second time: the 'while'-loop verifies again if there is 'guess' in the string 'tmpgword'.
that is true, and hence we go into the 'for-loop' again; and then into the 'if'-block ( the right char is found ) but here erase() don't work, and we enter in an infinite loop.
when the program finds the right index using 'for-loop', I break, and I start the search from the beginning in case there are more occurrences of guess.
the problem is: the program finds 'guess' again but erase() won't delete it!
can someone explain please. Here is my code:
while (tmpgword.find(guess,0) != string::npos )
{
for (i = 0; i < word.size(); i++) // verify the input;
{
if (word[i] == guess)
{
encword[i] = word[i];//I don't think this line is important
tmpgword.erase(tmpgword.begin() + i);
break;
}
}
}
After you do the first erase, the character positions in tmpgword are not the same as in word.
string::find() returns the position of the element when it's found, so you can use that instead of looping through word.
size_t pos = 0;
while ((pos = tmpgword.find(guess, pos)) != string::npos) {
tmpgword.erase(pos, 1);
}
I've used pos as the starting position for each call to find() so it starts from where it just erased, rather than searching from the beginning each time through (there can't be any occurrences before that, because they've all been erased).

Vector out of range- C++

I'm working on a project that requires me to make a game of Hangman in c++. I have most of it working, but I'm stuck at printing out the part of the word guessed correctly each time after the user enters a guess. I've created a class to represent a game of hangman, and in this class are the methods that determine what to do for a guess. If a guess is found in any location in the randomly chosen word from a dictionary, I save that char to the same location in a vector called currentWord. currentWord is initialized in the constructor to be contain "_" for the length of the randomly chosen word(that way it is the same size as the word and I can just update it as the user types a guess). For example, if the word is "semicolonialism", and the user's first guess is 'i', I want to replace the '_' in the currentWord vector with the letter 'i'.
string tempWord = word;
for (int i = 0; i < tempWord.size(); i++) {
u_long location = tempWord.find(guess);
currentWord->at(location) = tempWord[location];
tempWord[location] = '_';
}
What I've tried to do is store the member variable "word" in a temporary variable called tempWord. Then I iterate from 0 to the length of tempword. I use tempWord.find(guess) to find the location in tempWord that is a match for the guess, store that into a variable called location, and then update the currentWord vector at that location to equal the tempWord at that location. Since this would only work for the first time the matching char is found, I then change tempWord[location] to '_', that way the next time through, location will be different. But by doing this I some times get the out of range error. If I comment out
tempWord[location] = '_';
then I don't see this error, but only the first occurrence is replaced. Even though I get this out of bounds error, I can see in the debugger that each occurrence is properly replaced in the currentWord vector. This leaves me very confused, so any help would be greatly appreciated! Thanks
EDIT
Thanks to rapptz suggestion to check if location is equal to std::string::npos, I finally have it working. Here's the updated code segment with that check in place:
string tempWord = word;
for (int i = 0; i < tempWord.size(); i++) {
u_long location = tempWord.find(guess);
if (location != std::string::npos) {
currentWord->at(location) = tempWord[location];
tempWord[location] = '_';
}
}
I really liked Tristan's suggestion too, and will do that tomorrow most likely. Once I do, I'll post the updated code as well in case someone else might find it useful. Thanks again!
Was going to post this as a comment but it's easier in a bigger text box! You can avoid both the tempWord copy and the for loop like this:
std::string::size_type location = 0, start_pos = 0; // int would be fine, tbh
while ( (location = word.find(guess, start_pos)) != std::string::npos) {
currentWord.at(location) = word[location];
start_pos = location;
}
My guess is that tempword.find(guess) starts from 1 to the lenght of word, not 0. Please share that function too.

Adapting Boyer-Moore Implementation

I'm trying to adapt the Boyer-Moore c(++) Wikipedia implementation to get all of the matches of a pattern in a string. As it is, the Wikipedia implementation returns the first match. The main code looks like:
char* boyer_moore (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen) {
int i;
int delta1[ALPHABET_LEN];
int *delta2 = malloc(patlen * sizeof(int));
make_delta1(delta1, pat, patlen);
make_delta2(delta2, pat, patlen);
i = patlen-1;
while (i < stringlen) {
int j = patlen-1;
while (j >= 0 && (string[i] == pat[j])) {
--i;
--j;
}
if (j < 0) {
free(delta2);
return (string + i+1);
}
i += max(delta1[string[i]], delta2[j]);
}
free(delta2);
return NULL;
}
I have tried to modify the block after if (j < 0) to add the index to an array/vector and letting the outer loop continue, but it doesn't appear to be working. In testing the modified code I still only get a single match. Perhaps this implementation wasn't designed to return all matches, and it needs more than a few quick changes to do so? I don't understand the algorithm itself very well, so I'm not sure how to make this work. If anyone can point me in the right direction I would be grateful.
Note: The functions make_delta1 and make_delta2 are defined earlier in the source (check Wikipedia page), and the max() function call is actually a macro also defined earlier in the source.
Boyer-Moore's algorithm exploits the fact that when searching for, say, "HELLO WORLD" within a longer string, the letter you find in a given position restricts what can be found around that position if a match is to be found at all, sort of a Naval Battle game: if you find open sea at four cells from the border, you needn't test the four remaining cells in case there's a 5-cell carrier hiding there; there can't be.
If you found for example a 'D' in eleventh position, it might be the last letter of HELLO WORLD; but if you found a 'Q', 'Q' not being anywhere inside HELLO WORLD, this means that the searched-for string can't be anywhere in the first eleven characters, and you can avoid searching there altogether. A 'L' on the other hand might mean that HELLO WORLD is there, starting at position 11-3 (third letter of HELLO WORLD is a L), 11-4, or 11-10.
When searching, you keep track of these possibilities using the two delta arrays.
So when you find a pattern, you ought to do,
if (j < 0)
{
// Found a pattern from position i+1 to i+1+patlen
// Add vector or whatever is needed; check we don't overflow it.
if (index_size+1 >= index_counter)
{
index[index_counter] = 0;
return index_size;
}
index[index_counter++] = i+1;
// Reinitialize j to restart search
j = patlen-1;
// Reinitialize i to start at i+1+patlen
i += patlen +1; // (not completely sure of that +1)
// Do not free delta2
// free(delta2);
// Continue loop without altering i again
continue;
}
i += max(delta1[string[i]], delta2[j]);
}
free(delta2);
index[index_counter] = 0;
return index_counter;
This should return a zero-terminated list of indexes, provided you pass something like a size_t *indexes to the function.
The function will then return 0 (not found), index_size (too many matches) or the number of matches between 1 and index_size-1.
This allows for example to add additional matches without having to repeat the whole search for the already found (index_size-1) substrings; you increase num_indexes by new_num, realloc the indexes array, then pass to the function the new array at offset old_index_size-1, new_num as the new size, and the haystack string starting from the offset of match at index old_index_size-1 plus one (not, as I wrote in a previous revision, plus the length of the needle string; see comment).
This approach will report also overlapping matches, for example searching ana in banana will find b*ana*na and ban*ana*.
UPDATE
I tested the above and it appears to work. I modified the Wikipedia code by adding these two includes to keep gcc from grumbling
#include <stdio.h>
#include <string.h>
then I modified the if (j < 0) to simply output what it had found
if (j < 0) {
printf("Found %s at offset %d: %s\n", pat, i+1, string+i+1);
//free(delta2);
// return (string + i+1);
i += patlen + 1;
j = patlen - 1;
continue;
}
and finally I tested with this
int main(void)
{
char *s = "This is a string in which I am going to look for a string I will string along";
char *p = "string";
boyer_moore(s, strlen(s), p, strlen(p));
return 0;
}
and got, as expected:
Found string at offset 10: string in which I am going to look for a string I will string along
Found string at offset 51: string I will string along
Found string at offset 65: string along
If the string contains two overlapping sequences, BOTH are found:
char *s = "This is an andean andeandean andean trouble";
char *p = "andean";
Found andean at offset 11: andean andeandean andean trouble
Found andean at offset 18: andeandean andean trouble
Found andean at offset 22: andean andean trouble
Found andean at offset 29: andean trouble
To avoid overlapping matches, the quickest way is to not store the overlaps. It could be done in the function but it would mean to reinitialize the first delta vector and update the string pointer; we also would need to store a second i index as i2 to keep saved indexes from going nonmonotonic. It isn't worth it. Better:
if (j < 0) {
// We have found a patlen match at i+1
// Is it an overlap?
if (index && (indexes[index] + patlen < i+1))
{
// Yes, it is. So we don't store it.
// We could store the last of several overlaps
// It's not exactly trivial, though:
// searching 'anana' in 'Bananananana'
// finds FOUR matches, and the fourth is NOT overlapped
// with the first. So in case of overlap, if we want to keep
// the LAST of the bunch, we must save info somewhere else,
// say last_conflicting_overlap, and check twice.
// Then again, the third match (which is the last to overlap
// with the first) would overlap with the fourth.
// So the "return as many non overlapping matches as possible"
// is actually accomplished by doing NOTHING in this branch of the IF.
}
else
{
// Not an overlap, so store it.
indexes[++index] = i+1;
if (index == max_indexes) // Too many matches already found?
break; // Stop searching and return found so far
}
// Adapt i and j to keep searching
i += patlen + 1;
j = patlen - 1;
continue;
}

Do I need more space?

I have code that is supposed to separate a string into 3 length sections:
ABCDEFG should be ABC DEF G
However, I have an extremely long string and I keep getting the
terminate called without an active exception
When I cut the length of the string down, it seems to work. Do I need more space? I thought when using a string I didn't have to worry about space.
int main ()
{
string code, default_Code, start_C;
default_Code = "TCAATGTAACGCGCTACCCGGAGCTCTGGGCCCAAATTTCATCCACT";
start_C = "AUG";
code = default_Code;
for (double j = 0; j < code.length(); j++) { //insert spacing here
code.insert(j += 3, 1, ' ');
}
cout << code;
return 0;
}
Think about the case when code.length() == 2. You're inserting a space somewhere over the string. I'm not sure but it would be okay if for(int j=0; j+3 < code.length(); j++).
This is some fairly confusing code. You are looping through a string and looping until you reach the end of the string. However, inside the loop you are not only modifying the string you are looping through, but you also change the loop variable when you say j += 3.
It happens to work for any string with a multiple of 3 letters, but you are not correctly handling other cases.
Here is a working example of the for loop that is a bit more clear it what it's doing:
// We skip 4 each time because we added a space.
for (int j = 3; j < code.length(); j += 4)
{
code.insert(j, 1, ' ');
}
You are using an extremely inefficient method to do such an operation. Every time you insert a space you are moving all the remaining part of the string forward and this means that the total number of operations you will need is in the order of o(n**2).
You can instead do this transormation with a single o(n) pass by using a read-write approach:
// input string is assumed to be non-empty
std::string new_string((old_string.size()*4-1)/3);
int writeptr = 0, count = 0;
for (int readptr=0,n=old_string.size(); readptr<n; readptr++) {
new_string[writeptr++] = old_string[readptr];
if (++count == 3) {
count = 0;
new_string[writeptr++] = ' ';
}
}
A similar algorithm can be written also to work "inplace" instead of creating a new string, simply you have to first enlarge the string and then work backward.
Note also that while it's true that for a string you don't need to care about allocation and deallocation still there are limits about the size of a string object (even if probably you are not hitting them... your version is so slow that it would take forever to get to that point on a modern computer).