very basic C++ question. Looks I m really rusted there...
All I want to do is to read an array of X strings from a file and create an array of X vertical strings of the horizontal strings.
IE :
file contains:
azert
qsdfg
wxcvb
poiuy
mlkjh
I want to create a string array containing:
aqwpm
zsxol
edcol
rfvuj
tgbyh
Here is what I tried so far:
[bad code]
const int SIZE = 37;
std::string table_h[SIZE];
std::string table_v[SIZE];
int i = 0;
while (source >> table_h[i]) //,sizeof table_h[i]
{
for (int j = 0; j< SIZE; j++)
{
table_v[j][i] = table_h[i][j];
}
i++;
}
-> works fine for the first line, breaks when i=1. I don't understand what.
I noticed that although table_v[0][0] = 'f'; works fine.
Both table_v[0][36] = 'f'; and table_h[0].at(36); break.
With char * (which was my first idea),
char * table_h[SIZE];
char * table_v[SIZE];
something like
table_v[0][0] = 'f';
immediately breaks.
I suppose I need to allocate memory or initialize something first??
Thx in advance.
You should set the size of strings before using operator [] to access them. Resize of table_h is optional, but you definitely have to resize table_v.
const int SIZE = 37;
std::string table_h[SIZE];
std::string table_v[SIZE];
for (size_t i = 0; i < SIZE; ++i)
{
table_h[i].resize(SIZE);
table_v[i].resize(SIZE);
}
int i = 0;
while (source >> table_h[i])
{
for (int j = 0; j < SIZE; j++)
{
table_v[j][i] = table_h[i][j];
}
i++;
}
See the working example.
In my opinion, if you know the size of a strings, resizing is better than appending. It can save some memory re-allocations, and IMHO it is simply nicer solution.
Indeed the table_v[j] is an empty string.
The string needs to allocate space for the characters. This is not done by the index operators, i.e.
table_v[j][9] = 'a';
assumes enough space is allocated for table_v[j].
You can do append to your string to add to the initially empty string. Append does not take chars though, so instead of using index of table_h[i][j] you can use substr.
std::string to_append = table_j[i].substr(j, 1)
table[j].append(to_append);
This also relieves you of the i counter.
Here is a demonstrative program that shows how it can be done
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
int main()
{
std::vector<std::string> v1 =
{
"azert", "qsdfg", "wxcvb", "poiuy", "mlkjh"
};
for ( const std::string &s : v1 ) std::cout << s << ' ';
std::cout << std::endl;
auto max_size = std::accumulate( v1.begin(), v1.end(),
size_t( 0 ),
[]( size_t acc, const std::string &s )
{
return acc < s.size() ? s.size() : acc;
} );
std::vector<std::string> v2( max_size );
for ( const std::string &s : v1 )
{
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v2[i].push_back( s[i] );
}
}
for ( const std::string &s : v2 ) std::cout << s << ' ';
std::cout << std::endl;
return 0;
}
The program output is
azert qsdfg wxcvb poiuy mlkjh
aqwpm zsxol edcik rfvuj tgbyh
As for your code than these statements
std::string table_h[SIZE];
std::string table_v[SIZE];
defined two arrays of empty strings. So you may not apply the subscript opertaor to an empty string. You could use for example member function push_back
for (int j = 0; j< SIZE; j++)
{
table_v[j].push_back( table_h[i][j] );
}
Related
I've written a code that removes all vowels from a string in c++ but for some reason it doesn't remove the vowel 'o' for one particular input which is: zjuotps.
Here's the code:
#include<iostream>
#include<string>
using namespace std;
int main(){
string s;
cin >> s;
string a = "aeiouyAEIOUY";
for (int i = 0; i < s.length(); i++){
for(int j = 0; j < a.length(); j++){
if(s[i] == a[j]){
s.erase(s.begin() + i);
}
}
}
cout << s;
return 0;
}
When I input: zjuotps
The Output I get is: zjotps
This is a cleaner approach using the C++ standard library:
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main()
{
std::string input = "zjuotps";
std::string vowels = "aeiouyAEIOUY";
auto predicate = [&vowels](char c) { return vowels.find(c) != std::string::npos; };
auto iterator = std::remove_if(input.begin(), input.end(), predicate);
input.erase(iterator, input.end());
cout << input << endl;
}
Edit:
as #RemyLebeau pointed out, std::erase_if can be used which is introduced in c++20 and the answer becomes one line of code:
std::erase_if(input, [&vowels](char c) { return vowels.find(c) != std::string::npos; });
You can develop a solution by adding the matching characters to the new string object. The eliminate() method writes the character to the result object if the characters in the input object doesn't match the characters in the remove object.
#include <iostream>
/**
* #brief This method scans the characters in the "input" object and writes
* the characters not in the "remove" object to the "result" object.
* #param input This object contains the characters to be scanned.
* #param remove This object contains characters that will not match.
* #param result Non-match result data is writed to this object.
*/
void eliminate(std::string input, std::string remove, std::string &result);
int main()
{
std::string input = "zjuotpsUK", remove = "aeiouyAEIOUY", result;
eliminate(input, remove, result);
std::cout << result << std::endl;
return 0;
}
void eliminate(std::string input, std::string remove, std::string &result)
{
for (size_t i = 0, j = 0; i < input.length(); i++)
{
for(j = 0; j < remove.length(); j++)
if(input[i] == remove[j])
break;
if(j == remove.length())
result += input[i];
}
}
In your code here, I replaced s with input_str, and a with vowels, for readability:
for (int i = 0; i < input_str.length(); i++){
for(int j = 0; j < vowels.length(); j++){
if(input_str[i] == vowels[j]){
input_str.erase(input_str.begin() + i);
}
}
}
The problem with your current code above is that each time you erase a char in the input string, you should break out of the vowels j loop and start over again in the input string at the same i location, checking all vowels in the j loop again. This is because erasing a char left-shifts all chars which are located to the right, meaning that the same i location would now contain a new char to check since it just left-shifted into that position from one position to the right. Erroneously allowing i to increment means you skip that new char to check in that same i position, thereby leaving the 2nd vowel in the string if 2 vowels are in a row, for instance. Here is the fix to your immediate code from the question:
int i = 0;
while (i < s.length()){
bool char_is_a_vowel = false;
for(int j = 0; j < a.length(); j++){
if(s[i] == a[j]){
char_is_a_vowel = true;
break; // exit j loop
}
}
if (char_is_a_vowel){
s.erase(s.begin() + i);
continue; // Do NOT increment i below! Skip that.
}
i++;
}
However, there are many other, better ways to do this. I'll present some below. I personally find this most-upvoted code difficult to read, however. It requires extra study and looking up stuff to do something so simple. So, I'll show some alternative approaches to that answer.
Approach 1 of many: copy non-vowel chars to new string:
So, here is an alternative, simple, more-readable approach where you simply scan through all chars in the input string, check to see if the char is in the vowels string, and if it is not, you copy it to an output string since it is not a vowel:
Just the algorithm:
std::string output_str;
for (const char c : input_str) {
if (vowels.find(c) == std::string::npos) {
output_str.push_back(c);
}
}
Full, runnable example:
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <string>
int main()
{
std::string input_str = "zjuotps";
std::string vowels = "aeiouyAEIOUY";
std::string output_str;
for (const char c : input_str)
{
if (vowels.find(c) == std::string::npos)
{
// char `c` is NOT in the `vowels` string, so append it to the
// output string
output_str.push_back(c);
}
}
std::cout << "input_str = " << input_str << std::endl;
std::cout << "output_str = " << output_str << std::endl;
}
Output:
input_str = zjuotps
output_str = zjtps
Approach 2 of many: remove vowel chars in input string:
Alternatively, you could remove the vowel chars in-place as you originally tried to do. But, you must NOT increment the index, i, for the input string if the char is erased since erasing the vowel char left-shifs the remaining chars in the string, meaning that we need to check the same index location again the next iteration in order to read the next char. See the note in the comments below.
Just the algorithm:
size_t i = 0;
while (i < input_str.length()) {
char c = input_str[i];
if (vowels.find(c) != std::string::npos) {
input_str.erase(input_str.begin() + i);
continue;
}
i++;
}
Full, runnable example:
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <string>
int main()
{
std::string input_str = "zjuotps";
std::string vowels = "aeiouyAEIOUY";
std::cout << "BEFORE: input_str = " << input_str << std::endl;
size_t i = 0;
while (i < input_str.length())
{
char c = input_str[i];
if (vowels.find(c) != std::string::npos)
{
// char `c` IS in the `vowels` string, so remove it from the
// `input_str`
input_str.erase(input_str.begin() + i);
// do NOT increment `i` here since erasing the vowel char above just
// left-shifted the remaining chars in the string, meaning that we
// need to check the *same* index location again the next
// iteration!
continue;
}
i++;
}
std::cout << "AFTER: input_str = " << input_str << std::endl;
}
Output:
BEFORE: input_str = zjuotps
AFTER: input_str = zjtps
Approach 3 of many: high-speed C-style arrays: modify input string in-place
I borrowed this approach from "Approach 1" of my previous answer here: Removing elements from array in C
If you are ever in a situation where you need high-speed, I'd bet this is probably one of the fastest approaches. It uses C-style strings (char arrays). It scans through the input string, detecting any vowels. If it sees a char that is NOT a vowel, it copies it into the far left of the input string, thereby modifying the string in-place, filtering out all vowels. When done, it null-terminates the input string in the new location. In case you need a C++ std::string type in the end, I create one from the C-string when done.
Just the algorithm:
size_t i_write = 0;
for (size_t i_read = 0; i_read < ARRAY_LEN(input_str); i_read++) {
bool char_is_a_vowel = false;
for (size_t j = 0; j < ARRAY_LEN(input_str); j++) {
if (input_str[i_read] == vowels[j]) {
char_is_a_vowel = true;
break;
}
}
if (!char_is_a_vowel) {
input_str[i_write] = input_str[i_read];
i_write++;
}
}
input_str[i_write] = '\n';
Full, runnable example:
#include <iostream> // For `std::cin`, `std::cout`, `std::endl`, etc.
#include <string>
/// Get the number of elements in an array
#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))
int main()
{
char input_str[] = "zjuotps";
char vowels[] = "aeiouyAEIOUY";
std::cout << "BEFORE: input_str = " << input_str << std::endl;
// Iterate over all chars in the input string
size_t i_write = 0;
for (size_t i_read = 0; i_read < ARRAY_LEN(input_str); i_read++)
{
// Iterate over all chars in the vowels string. Only retain in the input
// string (copying chars into the left side of the input string) all
// chars which are NOT vowels!
bool char_is_a_vowel = false;
for (size_t j = 0; j < ARRAY_LEN(input_str); j++)
{
if (input_str[i_read] == vowels[j])
{
char_is_a_vowel = true;
break;
}
}
if (!char_is_a_vowel)
{
input_str[i_write] = input_str[i_read];
i_write++;
}
}
// null-terminate the input string at its new end location; the number of
// chars in it (its new length) is now equal to `i_write`!
input_str[i_write] = '\n';
std::cout << "AFTER: input_str = " << input_str << std::endl;
// Just in case you need it back in this form now:
std::string str(input_str);
std::cout << " C++ str = " << str << std::endl;
}
Output:
BEFORE: input_str = zjuotps
AFTER: input_str = zjtps
C++ str = zjtps
See also:
[a similar answer of mine in C] Removing elements from array in C
i am writing a function that will return the string with common prefix of all string in a given array.But i am not able to return the string as its size is still showing as zero.
string Solution::longestCommonPrefix(vector<string> &A) {
string s;
int k=A.size();
int m=A[0].size();
for(int i=1;i<k;i++)
{
int j=A[i].size();
if(j<m) m=j;
}
int f;
for(int i=0;i<k-1;i++)
{
for(f=0;f<m;f++)
{
if(A[i][f]==A[i+1][f])
{
s[f]=A[i][f];
}
else break;
}
cout<<s.size();
}
return s;
}
As I mentioned in the comments, the size of your string, s, is always zero since you are never actually changing its size. The size of a string can change in a few ways, through assignment (=, +=), and resize being two. Your program currently has undefined behaviour, since, when you do s[f]=A[i][f], you are modifying s at index f, even though s has no contents.
So how do you fix this? Initially, you can resize s to the size of m, and fill it temporarily with spaces. Now, there's still a logic problem in your if statement. Specifically, with the else, since if the else is reached, you need to cut off anything remaining, and change m. See the below for a full working program:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string longestCommonPrefix( const vector<string> &A ) {
string s;
int k = A.size();
int m = A[0].size();
for ( int i = 1; i < k; i++ ) {
int j = A[i].size();
if ( j < m ) m = j;
}
int f;
s.resize( m, ' ' ); // temporary, just to set the contents
for ( int i = 0; i < k - 1; i++ ) {
for ( f = 0; f < m; f++ ) {
if ( A[i][f] == A[i + 1][f] ) {
s[f] = A[i][f]; // copy the similar values
} else {
m = f; // if we reach a character which is not the same, then we reset m
s.resize( m ); // and resize the string
}
}
}
return s;
}
int main() {
vector<string> a;
a.push_back( "hello" );
a.push_back( "hell" );
a.push_back( "help" );
a.push_back( "he" );
cout << longestCommonPrefix( a );
getchar();
}
The above will have an output of he, which is the common prefix of all three words.
The below is a modified version of the function. First, I've replaced int with size_t, which should be used for any sizes. Second, it introduces a few error checks and simplifications. If the input vector is size 0, then don't do any checks and return an empty string. If the size is 1, then just return the contents of the element. No work needed.
After this, assume that A[0] is the entire prefix which belongs to all elements in the vector. In the for loop, you would then look for when this is not true, and resize the string accordingly. You will notice that the loop starts at 1, since we are assuming the first element is the full prefix. The inner loop also is less than s.size(), so that as s shrinks, it is accounted for.
string longestCommonPrefix( const vector<string> &A ) {
size_t k = A.size();
if ( k == 0 )
return "";
if ( k == 1 )
return A[0];
string s = A[0];
for ( size_t i = 1; i < k - 1; i++ ) {
if ( A[i].size() < s.size() ) s.resize( A[i].size() );
for ( size_t f = 0; f < s.size(); f++ ) {
if ( A[i][f] != A[i + 1][f] ) {
s.resize( f );
}
}
}
return s;
}
EDIT: There was an issue in the second version, with going out of bounds. UB again (oops). I added in a check, inside the first for loop, which now resizes s if it is larger than A[i]
You're indexing into a string that's uninitialized. When you do s[f]=A[i][f];, you're actually introducing Undefined Behavior into the mix - you might be writing to the string's memory or over something else entirely. No matter the behavior, you aren't triggering any of the string's internal counters that the C++ class exposes for you.
Instead, try doing s += A[i][f]. std::string exposes the += operator for you to append characters to an existing string. You can read about that and much more that your std::string can do here: http://www.cplusplus.com/reference/string/string/
As others have mentioned there is undefined behaviour at s[f]=A[i][f]; and also gave implementations, however Here is another version for c++14 or above with stl algorithms,
#include<algorithm>
#include<string>
#include<numeric>
#include<vector>
std::string longest_common_prefix(const std::vector<std::string>& v) {
if (v.size() == 0) {
return "";
}
auto begin = std::begin(v[0]), end = std::end(v[0]);
end = std::accumulate(std::begin(v) + 1, std::end(v), end, [begin](auto end, const auto& s) {
return std::mismatch(begin, end, std::begin(s), std::end(s)).first;
});
return std::string(begin, end);
}
I am trying to delete characters that aren't numbers nor letters from a string by such a stupid method since I failed in getting other methods ( I am a real beginner "just trying ")
I know that this way isn't the right one but my question is what the problem with it what is the error since it doesn't work :S
string g = "9-=p98u;iu8y76";
string y;
int t = 0;
for (int i = 0; i < g.length(); i++)
{
if (isdigit(g[i]) || isalpha(g[i]))
{
y[t++] = g[i];
}
else
continue;
}
g = y;
cout << g;
The problem is that the size of y is 0, it's empty. Accessing its elements (using y[t++]) therefore reaches "after" the string—it's buffer overflow, and Undefined Behaviour.
You need to extend y. To do this with minimal changes to your code, you'd do this:
string g = "9-=p98u;iu8y76";
string y;
for (int i = 0; i < g.length(); i++)
{
if (isdigit(g[i]) || isalpha(g[i]))
{
y.push_back(g[i]);
}
else
continue;
}
g = y;
cout << g;
Of course, there are other ways to do that. Using standard algorithms and the erase-remove idiom would be more idiomatic C++. The entire code can be replaced with this:
auto shouldBeRemoved = [](char c) { !(isdigit(c) || isalpha(c)) };
g.erase(std::remove_if(g.begin(), g.end(), shouldBeRemoved), g.end());
cout << g;
std::remove_if works by reorganising the range so that all elements which match the predicate (i.e. those which should be removed) are moved after all elements which are to remain. The member function erase then erases all of those which were moved to the back.
This is reasonably expressed by the standard library. Something like
auto digit_or_alpha = [](char c){ return isdigit(c) || isalpha(c); };
std::copy_if(g.begin(), g.end(), std::back_inserter(y), digit_or_alpha );
Should work. back_inserter is in <iterator>. Angew provides the reason why yours doesn't work.
The problem is the way you are trying to extend the string y. Indexing can be applied only in the domain of the string (i.e. you cannot index beyond the length of the string)
change y[t++] = g[i] to y += g[i]
Additionally, i would like to mention that you don't need the else branch. When execution reaches the end of loop scope, it will "automatically" continue, it isn't needed to be expressed explicitly.
PS: It's classic C++, not C++11, I would accept Captain Giraffe's answer
The general approach for such tasks is to use member function erase along with standard algorithm std::remove_if declared in header <algorithm>
For example
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main()
{
std::string s = "9-=p98u;iu8y76";
std::cout << s << std::endl;
s.erase( std::remove_if( s.begin(), s.end(),
[]( char c )
{
return !std::isalnum( ( unsigned char )c );
} ),
s.end() );
std::cout << s << std::endl;
return 0;
}
The program output is
9-=p98u;iu8y76
9p98uiu8y76
As for your code then you are trying to use the subscript operator
y[t++] = g[i];
for an empty string
string y;
Take into acoount that instead of two functions std::isalpha and std::isdigit you can use one function std::isalnum as shown in my demonstrative program.
If you want to write loops yourself then the program can look like
#include <iostream>
#include <string>
#include <cctype>
int main()
{
std::string s = "9-=p98u;iu8y76";
std::cout << s << std::endl;
std::string::size_type i = 0;
while ( i < s.length() && std::isalnum( ( unsigned char )s[i] ) ) ++i;
std::string::size_type j = i;
while ( i++ < s.length() )
{
if ( std::isalnum( ( unsigned char )s[i] ) ) s[j++] = s[i];
}
s.erase( j );
std::cout << s << std::endl;
return 0;
}
The program output is the same as above
9-=p98u;iu8y76
9p98uiu8y76
There is no need to use an additional string to do this operation.
I am building a small project for spelling correction, this is not homework.
Given two strings str1 and str2. One has to find out the number of characters matching between two strings.
For example if str1 = "assign" and str2 = "assingn", then the output should be 6.
In str2, characters, "a", "s", "s", "i", "g", "n" are there in str1, "assign". Thus output should be 6.
If str1 = "sisdirturn" and str2 = "disturb", then output should be 6.
In the str2, characters, "d", "i", "s", "t", "u", "r" are there in string str1, "sisdirturn". Thus output should be 6.
I've tried many attempts, however I am unable to get the answer. Kindly help to sort this out and if there is any idea to improve upon this, do tell.
Here is my attempt so far:
int char_match (string str1, string str2)
{
//Take two strings, split them into vector of characters and sort them.
int i, j, value = 0;
vector <char> size1, size2;
char* cstr1 = new char[str1.length() + 1];
strcpy(cstr1, str1.c_str());
char* cstr2 = new char[str2.length() + 1];
strcpy(cstr2, str2.c_str());
for(i = 0, j = 0 ; i < strlen(cstr1), j < strlen(cstr2); i++, j++)
{
size1.push_back( cstr1[i] );
size2.push_back( cstr2[j] );
}
sort (size1.begin(), size1.end() );
sort (size2.begin(), size2.end() );
//Start from beginning of two vectors. If characters are matched, pop them and reset the counters.
i = 0;
j = 0;
while ( !size1.empty() )
{
out :
while ( !size2.empty() )
{
if (size1[i] == size2[j])
{
value++;
pop_front(size1);
pop_front(size2);
i = 0;
j = 0;
goto out;
}
j++;
}
i++;
}
return value;
}
#include <iostream>
#include <algorithm> // sort, set_intersection
std::string::size_type matching_characters(std::string s1, std::string s2) {
sort(begin(s1), end(s1));
sort(begin(s2), end(s2));
std::string intersection;
std::set_intersection(begin(s1), end(s1), begin(s2), end(s2),
back_inserter(intersection));
return intersection.size();
}
int main() {
std::cout << matching_characters("assign", "assingn") << '\n'; // 6
std::cout << matching_characters("sisdirturn", "disturb") << '\n'; // 6
}
The above uses sort and so it has O(N*log N) performance, if that matters. If all your inputs are small then this may be faster than the second solution:
Sora's solution has better complexity, and can also be implemented concisely using standard <algorithm>s:
#include <iostream>
#include <algorithm> // for_each
#include <numeric> // inner_product
int matching_characters(std::string const &s1, std::string const &s2) {
int s1_char_frequencies[256] = {};
int s2_char_frequencies[256] = {};
for_each(begin(s1), end(s1),
[&](unsigned char c) { ++s1_char_frequencies[c]; });
for_each(begin(s2), end(s2),
[&](unsigned char c) { ++s2_char_frequencies[c]; });
return std::inner_product(std::begin(s1_char_frequencies),
std::end(s1_char_frequencies),
std::begin(s2_char_frequencies), 0, std::plus<>(),
[](auto l, auto r) { return std::min(l, r); });
}
int main() {
std::cout << matching_characters("assign", "assingn") << '\n'; // 6
std::cout << matching_characters("sisdirturn", "disturb") << '\n'; // 6
}
I'm using C++14 features, such as generic lambdas, for convenience. You may have to make some modifications if your compiler doesn't support C++14.
For me the solution using sort and set_intersection takes about 1/4th the time as the other solution for these inputs. That's because sorting and iterating over arrays of 6 or 7 elements can be faster than having to walk over arrays of 256 elements.
sort/set_intersection (3667ns) vs. for_each/inner_product (16,363ns)
Once the input is large enough the speed advantage will tip the other way. Furthermore, at the point where the input is too large to take advantage of the small-string optimization then the sort/set_intersection method will start doing expensive memory allocations.
Of course this performance result is highly implementation dependent, so if the performance of this routine matters you'll have to test it yourself on your target implementation with real input. If it doesn't matter then the O(N) solution is the better choice.
I am not 100% on what it is you are actually trying to achieve, but in the case of trying to see how many characters that match in the words, it would be a simple case of just running a loop through them and adding 1 every time you found a match, like this
int char_match (string str1, string str2)
{
//Take two strings, split them into vector of characters and sort them.
unsigned int matches = 0;
unsigned int stringLength = (str1.length > str2.length) ? str2.length : str1.length;
for(unsigned int i = 0; i < stringLength; ++i)
{
if(str1[i] == str2[i])
{
++matches;
}
}
return matches;
}
but from your code it looks like you want to find out exactly how many of the same characters they have that is to say ignoring the actual position of each character then it would be a rather different process. Something along the lines of this
int char_match (string str1, string str2)
{
unsigned int str1CharCount[256] = {0};
unsigned int str2CharCount[256] = {0};
unsigned int matches = 0;
for(unsigned int i = 0; i < str1.length; ++i)
{
++str1CharCount[static_cast<unsigned short>(str1[i])];
}
for(unsigned int i = 0; i < str2.length; ++i)
{
++str2CharCount[static_cast<unsigned short>(str1[i])];
}
for(unsigned int i = 0; i < 256; ++i)
{
matches += (str1CharCount[i] > str1CharCount[i]) ? str1CharCount[i] - (str1CharCount[i] - str2CharCount[i]) : str2CharCount[i] - (str2CharCount[i] - str1CharCount[i]);
}
return matches;
}
please note that for this second function there are probably a lot more efficient ways of doing it, but it should work all the same
EDIT:
This code should do what you wanted, main difference being it checks the ascii value to make sure it is a valid character
int char_match (string str1, string str2)
{
unsigned int str1CharCount[256] = {0};
unsigned int str2CharCount[256] = {0};
unsigned int matches = 0;
for(unsigned int i = 0; i < str1.length; ++i)
{
unsigned short aValue = static_cast<unsigned short>(str1[i]);
if(aValue >= static_cast<unsigned short>('a') && aValue <= static_cast<unsigned short>('z'))
{
++str1CharCount[static_cast<unsigned short>(str1[i]) - 32];
}
else if(aValue >= static_cast<unsigned short>('A') && aValue <= static_cast<unsigned short>('Z'))
{
++str1CharCount[static_cast<unsigned short>(str1[i])];
}
}
for(unsigned int i = 0; i < str2.length; ++i)
{
++str2CharCount[static_cast<unsigned short>(str1[i])];
}
for(unsigned int i = static_cast<unsigned short>('a'); i <= static_cast<unsigned short>('Z'); ++i)
{
matches += (str1CharCount[i] > str1CharCount[i]) ? str1CharCount[i] - (str1CharCount[i] - str2CharCount[i]) : str2CharCount[i] - (str2CharCount[i] - str1CharCount[i]);
}
return matches;
}
Hey I'm writing a function that takes two std::vector<std::string> and returns a third std::vector<std::string>.
The function is going to encode the two vectors together and create the 3rd vector.
I'm currently debugging this to find out why it's not working, and I keep getting: vector subscript out of range. As far as I can tell it's crashing at this line:
if (file2[i].size() < file1[i].size())
Can I use size() to get the size of the element at i?
std::vector<std::string> Encode(std::vector<std::string> &file1,
std::vector<std::string> &file2)
{
std::vector<std::string> file3;
std::string temp;
for (unsigned int i = 0; i < file1.size(); i++) {
for (unsigned int x = 0; x < file1[i].size(); x++) {
if (file2[i].size() < file1[i].size()) {
for (unsigned int t = 0; t < file2[i].size(); t++) {
file3[i][x] = (int)file1[i][x] + (int)file2[i][t];
}
} else if (file2[i].size() > file1[i].size()) {
file3[i][x] = (int)file1[i][x] + (int)file2[i][x];
}
if (file3[i][x] > 126) {
file3[i][x] = file3[i][x] % 127;
} else {
file3[i][x] = file3[i][x] + 32;
}
}
}
return file3;
}
Any idea what's going on here?
I'd be very much inclined to simplify by factoring. At the lowest layer is a combine function to combine two chars into one:
char combine(char a, char b)
{
char result = a+b;
if (result > 126)
return result % 127;
return result+32;
}
The next level up would be to iterate through each of the letters in two strings of possibly different sizes. The algorithm works for differing length strings by "recycling" through the shorter string.
std::string mix(const std::string &first, const std::string &second)
{
unsigned len1 = first.length();
unsigned len2 = second.length();
if (len1 < len2)
return mix(second, first);
std::string result;
// if the strings are of different lengths, first is now the longer
unsigned j=0;
for (unsigned i=0; i < len1; ++i, ++j) {
if (j >= len2)
j = 0;
result.push_back(combine(first[i], second[j]));
}
return result;
}
Finally, the combination of the vector of string is much simpler:
std::vector<std::string> Encode(const std::vector<std::string> &file1,
const std::vector<std::string> &file2)
{
std::vector<std::string> file3;
assert(file1.size() == file2.size());
for (unsigned int i = 0; i < file1.size(); i++) {
file3.push_back(mix(file1[i], file2[i]));
}
return file3;
}
Note that the code currently uses an assert to assure that the two vectors are the same length, but this is probably an artificial constraint. Real code should either assure that they are the same length or do something else to handle that case. Since it's not clear what your function is intended to do, I've left it to you to decide how to handle it, but with the assert as a placeholder to remind you that it does need to be addressed.
Finally, some driver code using C++11:
int main()
{
std::vector<std::string> english{"one", "two", "three", "four"};
std::vector<std::string> spanish{"uno", "dos", "tres", "cuatro"};
auto result = Encode(english, spanish);
std::copy(result.begin(), result.end(),
std::ostream_iterator<std::string>(std::cout, " "));
}
Note, too that I've used push_back to append to the end of the strings and const declarations for the passed strings.
Try these three sets of inputs:
1. file1 is bigger than file2
2. file2 is bigger than file1
3. file1 is equal to file2 in size.
Let us know the cases when the error was reproduced and when it was not.
I think by this stage you will solve the problem by yourself.
If not,
write contents of the (smallest possible) file1 and file2 that reproduced the error.
Some problems enumerated:
-You are assuming that file1 and file2 have the same size or at least that file1 has size <= file2 (in the other case would cause invalid memory access in line if (file2[i].size() < file1[i].size()) {) and are not checking in the function for that. At least add an assert statement or a checking.
-You are initializing file3 empty, and are indexing later in the function.
-The other problem is what happen when file1[i] and file2[i] have the same length, this option is not cover in the if-else, the cover options are < and > but not ==.
-You are accessing invalid memory with statements like file3[i][x], because the strings in file3[i] are initialized empty, for that, don't contain any character.
This is the more close that i can get without known the exact steps of the encode algorithm
#include <iostream>
#include <vector>
#include <boost/lexical_cast.hpp>
using namespace std;
std::vector<std::string> Encode(std::vector<std::string> &file1,
std::vector<std::string> &file2) {
assert(file1.size() <= file2.size());
std::vector<std::string> file3(file1.size());
std::string temp;
for (unsigned int i = 0; i < file1.size(); i++) {
for (unsigned int x = 0; x < file1[i].size(); x++) {
int enc = 0;
if (file2[i].size() <= file1[i].size()) {
for (unsigned int t = 0; t < file2[i].size(); t++) {
enc = (int)file1[i][x] + (int)file2[i][t];
}
}
else if (file2[i].size() > file1[i].size()) {
enc = (int)file1[i][x] + (int)file2[i][x];
}
if (enc > 126) {
file3[i] += (enc % 127);
}
else {
file3[i] += (enc + 32);
}
}
}
return file3;
}
int main(int argc, char *argv[]) {
std::vector<std::string> a{ "1", "2", "3" };
std::vector<std::string> b{ "6", "7", "8" };
for (const auto& s : a)
std::cout << s << std::endl;
for (const auto& s : b)
std::cout << s << std::endl;
auto c = Encode(a, b);
for (const auto& s : c)
std::cout << s << std::endl;
return 0;
}