Problem:
I was trying to encrypt a std::string password with a single rule:
Add "0" before and after a vowel
So that bAnanASplit becomes b0A0n0a0n0A0Spl0i0t.
However, I got stuck in an infinite loop.
Here is the code:
const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit";
//Add zeroes before and after vowels
for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
std::cout << pass << "\n";
if(i != std::string::npos)
{
std::cout << pass[i] << ": " << i << "\n";
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}
}
...And the result:
bAnanASplit
A: 1
b0A0nanASplit
a: 5
b0A0n0a0nASplit
A: 9
b0A0n0a0n0A0Split
i: 15
b0A0n0a0n0A0Spl0i0t
b0A0n0a0n0A0Spl0i0t
A: 2
b00A00n0a0n0A0Spl0i0t
a: 8
b00A00n00a00n0A0Spl0i0t
A: 14
b00A00n00a00n00A00Spl0i0t
i: 22
b00A00n00a00n00A00Spl00i00t
b00A00n00a00n00A00Spl00i00t
...
Any help? This sure seems strange.
Edit: All the answers were useful, and therefore I have accepted the one which I think best answers the question. However, the best way to solve the problem is shown in this answer.
Never, ever, modify the collection/container you are iterating upon!
Saves you a lot of trouble that way.
Let's start with your code and generate a new string with vowels surrounded by 0.
const std::string VOWELS = "AEIOUaeiou";
std::string pass = "bAnanASplit", replacement;
//Add zeroes before and after vowels
for (auto ch : pass)
{
if(VOWELS.find(ch) != std::string::npos)
replacement += '0' + ch + '0';
else
replacement += ch;
}
And there you have it!
As the OP seems to look for the exact reason for the misbehavior, I thought to add another answer as the existing answers do not show the exact issue.
The reason for the unexpected behavior is visible in following lines.
for (int i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
...
Problem 1:
The loop counter i is an int (i.e. a signed int). But std::string::find_first_of returns std::string::npos if there's no match. This is usually the maximum number representable by an unsigned long. Assigning a huge unsigned value to a shorter signed variable will store a totally unexpected value (assuming you are not aware of that). In this case, i will becomes -1 in most platforms (try int k = std::string::npos; and print k if you need to be sure). i = -1 is valid state for the loop condition i < pass.length(), so the next iteration will be allowed.
Problem 2:
Closely related to the above problem, same variable i is used to define the start position for the find operation. But, as explained, i will not represent the index of the character as you would expect.
Solution:
Storing a malformed value can be solved by using the proper data type. In the current scenario, best options would be using std::string::size_type as this is always guaranteed to work (most probably this will be equal to size_t everywhere). To make the program work with the given logic, you will also have to use a different variable to store the find result.
However, a better solution would be using a std::stringstream for building the string. This will perform better than modifying a string by inserting characters in the middle.
e.g.
#include <iostream>
#include <sstream>
int main() {
using namespace std;
const string VOWELS = "AEIOUaeiou";
const string pass = "bAnanASplit";
stringstream ss;
for (const char pas : pass) {
if (VOWELS.find(pas) == std::string::npos) {
ss << pas;
} else {
ss << '0' << pas << '0';
}
}
cout << pass << "\n";
cout << ss.str() << endl;
}
You are not exiting the loop in case i becomes std::string::npos. So, the i value is changed to some unexpected value (likely something like -1) when it gets to the position of last i or 0 after i(here I am referring to i of split). This is because i is an signed integer but in this case find_first_of() returns std::string::npos which is largest value that can be held by a size_t. In that case the terminating condition i < pass.length() may hold true and the loop continues. So, I am recommending following changes in your code -
for (size_t i = 0; i < pass.length(); ++i)
{
i = pass.find_first_of(VOWELS, i);
if(i == std::string::npos)
break;
pass.insert(pass.begin() + i++, '0');
pass.insert(pass.begin() + ++i, '0');
}
On the same note if (i != std::String::npos) does not do what you are expecting it to do.
But then again it better not to modify the container while you are iterating over it which #Tanveer mentioned in his answer
Related
I use atoi() to convert argv to int, the problem is when argv = zero is a considered parameter in my problem, it will return zero as well.
I tried to loop about all the arguments except file name for sure, what to do ?
for (int i = 2; i < argc; i++) {
if (!atoi(argv[i]) && atoi(argv[i]) != zero) {
std::cout << "invalid" << std::endl;
return 0;
}
}
I used few cases like == zero, || instead of && and so on, same problem always .. I even used strtol but same problem as well, I dunno if it's a problem with the algorithm, but I thought about it for a long time.
edit : my arguments are like next : a1.exe add 3 2 1 4 .. I started i = 2 to begin with 3, my arguments are all numbers. the atoi returns 0 if an argument is letter or zero so I want zero to be accepted as a parameter since it's a number. Sorry if I sounded vague or something
reason for editing : people misunderstood my question.
If you want to convert a number and know wether the input string was correct, then you can use the std::strtol family of functions.
http://www.cplusplus.com/reference/cstdlib/strtol/
const char *val = "123abc";
char *end = nullptr;
int i = std::strtol(val, &end, 10);
if (end != &val[strlen(val)])
std::cout << "invalid characters in number" << std::endl;
end points to the first character not converted, so if the whole string is a valid number, it points to the end of it.
You can do the following.
SO your Problem is that you can't determine if your argv[i] contains either a numeric 0 or a letter or other sign.
for (int i = 2; i < argc; i++) {
for(int x = 0; x < strlen(argv[i])){
if(!isDigit(argv[i][x])){
std::cout << "invalid" << std::endl;
return 0;
}
}
}
In this example we get the size of the actual char array that's contained inside of argv[i] than we check every single char if it is a digit.
If it's not we write "invalid" onto the console and return 0;
ofc you probably need to make some exceptions but I think this could be a decent start and help you with your problem
I'm trying to make a program which modifies words in a specific manner:
It should first check the ending of the words and then proceed to modify them. I won't explain it in detail, because it doesn't make much sense in English.
I've written the following:
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Por favor, introduzca los gentilicios separados por la tecla enter, para finalizar, escriba OK" << '\n';
string name[10];
string place[10];
for (int i(0); (i < 10); i++)
{
getline(cin, name[i]);
if (name[i] == "OK") //Error here
break;
}
for (int i(0); (i < 10); i++)
{
place[i] = name[i];
if (name[i][name[i].length() - 1] == 'c')
{
if (name[i][name[i].length()] == 'a' || (name[i][name[i].length()] == 'o') || (name[i][name[i].length()] == 'u'))
place[i][place[i].length() - 1] = 'q';
place[i][place[i].length()] = 'u';
place[i] = place[i] + "istan";
}
else if (name[i][name[i].length()] == 'a' || name[i][name[i].length()] == 'e' || name[i][name[i].length()] == 'i' || name[i][name[i].length()] == 'o' || name[i][name[i].length()] == 'u')
{
place[i][place[i].length()] = 'i';
place[i] = place[i] + "stan";
}
if (name[i][name[i].length()] == 's')
place[i] = place[i] + "tan";
else {
place[i] = place[i] + "istan";
}
place[i][0] = toupper(place[i][0]);
}
for (int i(0); (i < 10); i++)
{
cout << place[i] << '\n';
}
return 0;
}
Now I'm getting the error "String subscript out of range" . I would like to know where is the error exactly. I know it prompts when I write "OK", at line "18".
The condition i <= sizeof(name). sizeof(name) returns the size of the array in bytes, not the number of elements in it. Even if it returned the number of elements, <= is wrong and would cause an out-of-bounds access (should be <).
To loop through all elements in an array, you can use the range-based for-loop:
for(auto& n : name)
{
getline(cin, n);
if (n == "OK")
break;
}
Or to do it the right way with the C-style for-loop:
for (int i(0); i < sizeof(name)/sizeof(name[0]; i++)
{
…
}
Here:
for (int i(0); (i <= sizeof(name)); i++)
sizeof(name) is the size in bytes of the array, which as it is an array of std::string is effectively meaningless. If you want to iterate over 10 items, simply say so (note also that less-than-or-equals is also wrong here):
for (int i = 0; i < 10; i++)
And here:
getline(cin, name[i]);
whenever you perform input you must check the return value of the input function and handle any errors:
if( ! getline(cin, name[i]) ) {
// handle error somehow
}
And here:
string * p;
you do not want to be dealing with pointers to strings. If you want to access the contents of a string, you use operator[] or other string member functions on the string.
std::strings are not like cstrings. You can just grab a part of them using a std::string*. When you do
*(p+ (name[i].length()-2))
You actually say advance the address stored in p by name[i].length()-2 amount and access that string. If you go past the end of the name array then that is undefined behavior. If not you still haver a std::string which cannot be compared with a char. If you want to check if the string ends with "ca" then you can just use
if (name[i].substr(name[i].size() - 2) == "ca")
You're last loop is doing something quite funky. There's no need to go that far. You can just do something like:
if (name[i][name[i].length - 2] == 'c')
To compare the next to last character with c. And a very similar test to compare the last one with a.
To clarify why what you're doing is not OK, you first get p as a pointer to a string to the current element. Then you do some pointer arithmetic p + (name[i].length - 2), which still results in a pointer to a string. Finally, you dereference this, resulting in a string. Which you can't compare to a char. Moreover, the pointer was to some arbitrary address in memory, so the dereference would produce a string with very bad data in it. Quite arbitrary, one might say. If you tried to work with it you'd break your program
You seem to be working with the string as one would with a C-like string, a char*. The two are not the same, even though they represent the same concepts. A C++ string, usually, has a size field, and a char* pointer inside it, as well as a bunch of other logic to make working with it a char-m.
Because you aren't comparing against a specific char in the string, you're comparing against a string.
Considering the following bit of code:
*(p + (name[i].length() - 2))
This evaluates to a string because you are taking p (a string*) and concatenating a char to it. This means it's still a string (even though it's a one-character string), thus the other side of the equation won't be comparable to it.
What you need here instead is this:
if (name[i][name[i].length() - 2] == 'c')
Since name[i] is already a string, we can just get the char from it using the code above. This does return char, so it's comparable. This also allows you to get rid of the whole string* bit as it is not needed.
First, (i <= sizeof(name)) is wrong, it should be i < sizeof(name) / sizeof(*name). sizeof(array) return the size of array in bytes, you need to divide the size of an array's element to actually get the maximum element count of an array. If you find that complicated then use std::vector:
vector<string> name(10); //a vector of size 10
for (size_t i = 0; i < name.size(); i++) //name.size(), simple
Secondly, you need to keep track of how many strings in your name array. Or you need to check if name[i] == "OK" then break the second loop (similar to the first loop). name[i] after "OK" are invalid.
Thirdly, don't use *(p+ (name[i].length()-2)). If you want the second last character of name[i], you can write it as name[i][name[i].size()-2] or name[i].end()[-2] or end(name[i])[-2]
If you want to check if the word ends in "ca", then you can use substr:
if (name[i].substr(name[i].size() - 2) == "ca")
{
//...
}
I recently did this question
Specification:
Input Format The first line contains the number of test cases, T. Next,
T lines follow each containing a long string S.
Output Format For each long string S, display the number of times SUVO
and SUVOJIT appears in it.
I wrote the following code for this :
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int suvo = 0;
int suvojit = 0;
string s;
cin >> s;
for (int i = 0; i <= s.size() - 7; i++) {
if (s.substr(i, 7) == "SUVOJIT")
suvojit++;
}
for (int i = 0; i <= s.size() - 4; i++) {
if (s.substr(i, 4) == "SUVO")
suvo++;
}
cout << "SUVO = " << suvo - suvojit << ", SUVOJIT = " << suvojit << "\n";
}
return 0;
}
The code about gave out of bounds exception for substr() function for this test case:
15
RSUVOYDSUVOJITNSUVOUSUVOJITESUVOSUVOSGSUVOKSUVOJIT
SUVOJITWSUVOSUVOJITTSUVOCKSUVOJITNSUVOSUVOJITSUVOJITSUVOSUVOSUVOJITTSUVOJ
SUVOSUVOSUVOJITASUVOJITGCEBISUVOJITKJSUVORSUVOQCGVHRQLFSUVOOHPFNJTNSUVOJITKSSUVO
SUVOJITSUVOJITJGKSUVOJITISUVOJITKJLUSUVOJITUBSUVOX
MMHBSUVOFSUVOFMSUVOJITUMSUVOJITPSVYBYPMCSUVOJIT
OASUVOSUVOJITSUVOSTDYYJSUVOJITSUVOJITSUVO
RLSUVOCPSUVOJITYSUVOSUVOOGSUVOOESUVOJITMSUVO
WVLFFSUVOJITSUVOVSUVORLESUVOJITPSUVOJITSUVO
RSUVOSUVOJITQWSUVOUMASUVOSUVOJITXNNRRUNUSUVOJIT
HYLSSUVOSUVOSUVOJITPOSUVOJIT
DGMUCSSSUVOJITMJSUVOHSUVOCWTGSUVOJIT
OBNSSUVOYSUVOSUVOJITSUVOJITRHFDSUVODSUVOJITEGSUVOSUVOSUVOJITSUVOSUVOJITSSUVOSUVOSUVOSSUVOJIT
AG
NSUVOJITSUVOSUVOJIT
CGJGDSUVOEASUVOJITSGSUVO
However, when instead of using the s.size() function, I converted the string into a char constant and took the length of it using strlen, then the code caused no error and everything went smoothly.
So, my question is... Why did this happen?
This is my working code with the change:
#include <bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int suvo = 0;
int suvojit = 0;
string s;
cin >> s;
int le = strlen(&s[0]);
for (int i = 0; i <= le - 7; i++) {
if (s.substr(i, 7) == "SUVOJIT")
suvojit++;
}
for (int i = 0; i <= le - 4; i++) {
if (s.substr(i, 4) == "SUVO")
suvo++;
}
cout << "SUVO = " << suvo - suvojit << ", SUVOJIT = " << suvojit << "\n";
}
return 0;
}
In one case, you use size_t, in the other case you use int.
If the length is for example 6 characters, then s.size () - 7 is not -1, but one huge number and everything goes wrong. But if you write int len = strlen (...), then len - 7 is indeed -1 and everything is fine.
When I see a number subtracted from size_t, that's an immediate red flag. Write "i + 7 ≤ s.size()", not "i ≤ s.size() - 7".
First of all, in my testing your second leads to a problem as well:
Second, especially with older compilers (well, libraries, really) this can be horrendously inefficient, creating a huge number of temporary strings that you only use to compare with another string1.
So, let's consider how the job should be done instead. std::string has a member named find for situations like this. It returns the position of one string inside another, or std::string::npos if there is none. It allows you to specify a starting position at which to begin searching, when you don't want to start from the beginning.
We also, of course, have two instances of essentially identical code, once to search for SUVO, the other to search for SUVOJIT. The code would be much better off with the search code moved into a function, so we only have the search code in one place.
int count_pos(std::string const &haystack, std::string const &needle) {
size_t pos = 0;
int ret = 0;
while ((pos = haystack.find(needle, pos)) != std::string::npos) {
++ret;
++pos;
}
return ret;
}
Note that this also eliminates quite a bit more messy "stuff" like having to compute the maximum possible position at which at match could take place.
1. Why does compiler/library age matter? Older libraries often used a COW string that dynamically allocated storage for every string. More recent ones typically include what's called a "short string optimization", where storage for a short string is allocated inside the string object itself, avoiding the dynamic allocation.
I've got a std::string number = "55353" and I want to extract the numbers that I've used in this string (5 and 3). Is there a function to do that? If so, please tell me it's name, I've been searching for quite a while now and still haven't found it...
UPD:
I've solved my problem (kinda)
std::string number(std::to_string(num));
std::string mas = "---------";
int k = 0;
for (int i = 0; i < number.size(); i++) {
char check = number[i];
for (int j = 0; j < mas.size(); j++) {
if (check == mas[j])
break;
if (check != mas[j] && check != mas[j+1]) {
mas[k] = check;
k++;
break;
}
}
}
mas.resize(k); mas.shrink_to_fit();
std::string mas will contain numbers that were used in std::string number which is a number converted to std::string using std::to_string().
Try this:
std::string test_data= "55335";
char digit_to_delete = '5';
unsigned int position = test_data.find();
test_data.erase(position, 1);
cout << "The changed string: " << test_data << "\n";
The algorithm is to find the number (as a character) within the string. The position is then used to erase the digit in the string.
Your question looks like homework, so I can guess what you forgot to tell us.
mas starts with ten -. If you spot a 5, you should replace the 6th (!) dash with a '5'. That "6th" is just an artifact of English. C++ starts to count at zero, not one. The position for zero is mas[0], the first element of the array.
The one tricky bit is to understand that characters in a string aren't numbers. The proper term for them is "(decimal) digits". And to get their numerical value, you have to subtract '0' - the character zero. So '5' - '0' == 5 - the character five minus the character zero is the number 5.
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
using namespace std;
static bool isanagram(string a, string b);
int main(void)
{
int i,n,j,s;
cin >> n;
string a, b;
cin >> a >> b;
if(!isanagram(a,b)) cout << "False" << endl;
else cout << "True" << endl;
return 0;
}
static bool isanagram(string a, string b)
{
int i, j, size, s=0;
size = a.size();
bool k;
for(i=0;i<size;i++)
{
k=false;
for(j=0;j<size;j++)
{
if(a[i] == b[j]) { k = true; break; }
}
if(k==true) s+=1;
}
cout << a[2] << b[2] << endl;
if(s == size) return true;
else return false;
}
I don't know where exactly is the problem so i just pasted the whole code.
It should be a simple program capable for finding if two strings are anagrams, but it's not working and i don't know why. I used pointers in the program so thought the might be the problem and removed them, i removed other things additionally but still it's not working. If you can give it a look-see and tell me some idea where i might've gone wrong with my code ?
Thank you in advance.
The logic for your isanagram function is fatally flawed - it will never work correctly, even if you manage to fix the bugs in it.
You need to make sure that you have a correct algorithm before you start coding. One simple algorithm might be:
sort a
sort b
isanagram = (a == b)
It's not always return true:
Here's my input:
0
sdf
fda
Here's output I got:
fa
False
Regarding your task: if performance is not an issue for you task, just sort 2 strings (using std::sort) and compare results.
Regarding your style:
use string::length() instead of size() -- it's more idiomatic
instead of if(s == size) return true; else return false; consider return s == size
pass your strings by const reference, not by value
consider declaring variables as close to point of their usage as possible (but not closely) and initialize them when declaring (i, j, k, size all fit this hint)
Your approach is fine but it has a small flaw. You ensuring that every char from string a is present in string. So if a = "aab" and b = "abc", your approach will flag them as anagram. You also need to take the count of char in account.
The definition of anagram is:
An anagram is a type of word play, the result of rearranging the letters of a word or phrase to produce a new word or phrase, using all the original letters exactly once;
Easiest way as many have suggested is to ensure that the strings are of the same length . If they are, sort the two string and check for equality.
If you want to patch your approach, you can make the char in string b NULL after it has been matched with a char in string a.
Something like:
if(a[i] == b[j]) { b[j] = 0; k = true; break; }
in place of your:
if(a[i] == b[j]) { k = true; break; }
This way once a char of b has been matched it cannot participate again.
There are essentially two ways of checking for anagrams:
Sort both strings and see if they match. If they are anagrams, they will both have the same letters and a sort would order them into the same sequence.
Count the frequency of each char in each string. If they are anagrams, the frequency counts for each char will be the same for both strings.
First things first: don't declare the method static. It's a confusing keyword at the best of times given all the roles it can fulfill... so reserve for times when you really have to (method or attribute of a class that is not tied to any instance for example).
Regarding the algorithm: you're nearly there, but presence only is not sufficient, you need to take the number of characters in account too.
Let's do it simply:
bool anagram(std::string const& lhs, std::string const& rhs)
{
if (lhs.size() != rhs.size()) return false; // does not cost much...
std::vector<int> count(256, 0); // count of characters
for (size_t i = 0, max = lhs.size(); i != max; ++i)
{
++count[lhs[i]];
--count[rhs[i]];
}
for (size_t i = 0, max = count.size(); i != max; ++i)
if (count[i] != 0) return false;
return true;
} // anagram
Let's see it at work: anagram("abc","cab")
Initialization: count = [0, 0, ...., 0]
First loop i == 0 > ['a': 1, 'c': -1]
First loop i == 1 > ['a': 0, 'b': 1, 'c': -1]
First loop i == 2 > ['a': 0, 'b': 0, 'c': 0 ]
And the second loop will pass without any problem.
Variants include maintaining 2 counts arrays (one for each strings) and then comparing them. It's slightly less efficient... does not really matter though.
int main(int argc, char* argv[])
{
if (argc != 3) std::cout << "Usage: Program Word1 Word2" << std::endl;
else std::cout << argv[1] << " and " << argv[2] << " are "
<< (anagram(argv[1], argv[2]) ? "" : "not ")
<< "anagrams" << std::endl;
}
I see some problems with your code. Basically the algorithm is wrong. It will match characters within a.size(). It takes no account for duplicates (in either a or b).
Essentially, you should sort the strings and then compare for equality.
If you can't sort, at least remove the b characters from the comparison, eliminate the k variable.