I have this function that checks if a string is a substring, is it possible for me to add a variable to it that will count to me how many times that subsequence appears in that sequence? or i need to create another function for that.
bool SearchString(string sir1, string sir2) {
if (sir2.size() > sir1.size())
return false;
for (int i = 0; i < sir1.size(); i++) {
int j = 0;
if (sir1[i] == sir2[j]) {
int k = i;
while (sir1[i] == sir2[j] && j < sir2.size()) {
j++;
i++;
}
if (j == sir2.size())
return true;
else
i = k;
}
}
return false;
}
As "500 - Internal Server Error" said, you simply need to increment a counter where you return. With a little bit of refactoring it would look like this:
unsigned SearchString(const string& haystack, const string& needle) {
if (needle.size() > haystack.size())
return 0;
unsigned count = 0;
for (int i = 0; i < haystack.size(); ++i) {
int j = 0;
if (haystack[i] == needle[j]) {
int k = i;
while (k < haystack.size() && j < needle.size() && haystack[k] == needle[j]) {
++j;
++k;
}
if (j == needle.size())
++count;
}
}
return count;
}
Note: it's important to check that you haven't reached the end of the haystack while searching for the needle. Consider haystack="ababa", needle="bac": trying to locate 'c' would read after the end of the haystack
It's also important to check reaching end before trying to dereference the next character:
while (sir1[i] == sir2[j] && j < sir2.size()) ...
would read sir2[j] before making sure j is not over the boundary.
For starters your function SearchString is too complicated and moreover can invoke undefined behavior in this loop
while (sir1[i] == sir2[j] && j < sir2.size()) {
j++;
i++;
}
if for example when the string s2 contains an imbedded zero character '\0'.
Also the function parameters should have constant referenced types.
The function can be written much simpler.
As for your question then it will be better to write a separate function to count occurrences of a sub-string.
That is there is no great sense to count all occurrences of a sub-string in a string if you need only to know whether the sub-string is present in the string.
Here is a demonstration program
#include <iostream>
#include <iomanip>
#include <string>
bool SearchString( const std::string &s1, const std::string &s2 )
{
return s1.find( s2 ) != std::string::npos;
}
size_t CountStringOccurrences( const std::string &s1, const std::string &s2 )
{
size_t n = 0;
for ( std::string::size_type pos = 0;
s1.find( s2, pos ) != std::string::npos;
pos += s2.size() )
{
++n;
}
return n;
}
int main()
{
std::string s1( "123123123" );
std::string s2( "123" );
std::cout << std::boolalpha << SearchString( s1, s2 ) << '\n';
std::cout << CountStringOccurrences( s1, s2 ) << '\n';
return 0;
}
The program output is
true
3
This sounds like std::search as in
bool SearchString(const std::string& s0, const std::string& s1) {
return std::search(s0.begin(), s0.end(), s1.begin(), s1.end()) != s0.end();
}
Counting would be a loop:
std::size_t count = 0;
for (auto it = std::search(s0.begin(), s0.end(), s1.begin(), s1.end());
it != s0.end();
it = std::search(it, s0.end(), s1.begin(), s1.end())) {
++count;
}
or perhaps clearer: pull out the duplication:
std::size_t count = 0;
auto search = [&](auto start) { return std::search(start, s0.end(), s1.begin(), s1.end()); };
for (auto it = search(s0.begin()); it != s0.end();
it = search(it)) {
++count;
}
I bet there’s a way to do it with std::ranges::count_if but I don’t see it.
Related
I am experiencing a bug in my submissions for Leetcode 28 that has thus far eluded me. My code works for most test cases but I am getting hung up on scenarios such as haystack = "mississippi", needle = "issip".
I have tried debugging and found that the entire haystack string is iterated through and it is returning -1 or not found. The substring length it is finding at each occurrence of 'i' is 4, 1, 1.
int strStr(string haystack, string needle) {
if (needle.empty()) {
return 0;
}
if (haystack.empty() && !needle.empty()) {
return -1;
}
int i = 0, j = 0, ans = 0;
for (i; i < haystack.length(); i++) {
if (haystack[i] == needle[0]) {
j = 0;
ans = i;
for (j; j < needle.length(); j++) {
/*
if (haystack[i++] == needle[j]) {
continue;
}
else {
break;
}
*/
if (haystack[i++] != needle[j]) {
break;
}
}
if (j == needle.length()) {
return ans;
}
}
if (j == needle.length()) {
return ans;
}
}
return -1;
}
Input: "mississippi", "issip"
Output: -1 (ans = 10, j = 1)
The function has several drawbacks.
For starters it should be declared like
std::string::size_type strStr( const std::string &haystack, const std::string &needle );
and if the second string is not found in the first string the function should return std::string::npos as all similar member functions of the class std::string do.
The function parameters shell be of constant referenced types.
The condition in this if-statement
if (haystack.empty() && !needle.empty())
has a redundant operand. It could be rewritten like
if (haystack.empty())
This loop
for (i; i < haystack.length(); i++)
should stop its iterations when the size of the tail of the first string is less than the size of the second string.
in this if-statement
if (haystack[i++] != needle[j]) {
the variable i is incremented that results in incrementing the variable two times: one in this statement and the second time in the loop.
The second pair of these statements
if (j == needle.length()) {
return ans;
is redundant.
The function can be written the following way as it is shown in the demonstrative program.
#include <iostream>
#include <string>
std::string::size_type strStr( const std::string &haystack, const std::string &needle )
{
if ( needle.empty() )
{
return 0;
}
else if ( haystack.empty() )
{
return -std::string::npos;
}
else
{
std::string::size_type ans = std::string::npos;
auto n1 = haystack.length();
auto n2 = needle.length();
for ( std::string::size_type i = 0; ans == std::string::npos && i + n2 <= n1; i++ )
{
std::string::size_type j = 0;
while ( j < n2 && haystack[i+j] == needle[j] ) j++;
if ( j == n2 ) ans = i;
}
return ans;
}
}
int main()
{
std::string haystack( "mississippi" );
std::string needle( "issip" );
std::cout << strStr( haystack, needle ) << '\n';
return 0;
}
Its output is
4
The problem is that you modify i in
if (haystack[i++] != needle[j]) {
Thus preventing a second potential match from being explored. Try
if (haystack[i + j] != needle[j]) {
and fix any knock-on issues. I expect it to work as-is, though.
I have a text file with a list of words.
I used ifstream to read these words into a vector and now I am trying to sort them in an order similar to:
A a B b C c [...]
I tried to implement this using a third for loop inside of a bubble search algorithm to look at the first character of each word (I know this is far from the most efficient way especially if I was using a large data set)
And then check whether the letter and the next letter were uppercase or lowercase and switching if the uppercase letter was the same letter as the current letter, but this didn't seem to work.
void bubble_Sort (vector <string> & words)
{
for (unsigned i = words.size(); i >= 2; --i)
{
for (unsigned k = 0; k + 1 < i; k++)
{
int hi = k+1;
string temp1 = words[hi];
string temp2 = words[k];
int smallsize = words[hi].size();
int smallprecedence = 0;
if (words[k].size() < words[hi].size())
smallsize = words[k].size();
for (unsigned j = 0; j < smallsize; j++)
{
if (temp1[j] >= 'A' && temp1[j] <= 'Z')
{
if (temp2[j] >='a' && temp2[j] <= 'z')
{
char lowercase1 = temp1[j] + 32;
if (lowercase1 == temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
else if (temp2[j] >= 'A' && temp2[j] <= 'Z')
{
if (temp1[j] < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
}
if (temp1[j] >= 'a' && temp1[j] <= 'z')
{
if (temp2[j] >= 'A' && temp2[j] <= 'Z')
{
char uppercase1 = temp1[j] - 32;
if (uppercase1 < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
else if (temp2[j] >= 'a' && temp2[j] <= 'z')
{
if (temp1[j] < temp2[j])
{
string temp = words[k];
words[k] = words[hi];
words[hi] = temp;
break;
}
}
}
else if (temp1[j] == temp2[j] && temp1.size() < temp2.size())
++smallprecedence;
}
if (smallprecedence == smallsize)
{
string temporary = words[k];
words[k] = words[hi];
words[hi] = temporary;
}
}
}
}
Don't reinvent the wheel. Just modify the default comparison function so aA < bB (regardless of case) and A < a.
EDIT I used the wrong comparison function. It should return true for <, and false for >=. This has been fixed
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
const char* s1=lhs.c_str();
const char* s2=rhs.c_str();
while(true) {
// first ignore case
if ( std::toupper(*s1) < std::toupper(*s2) ) return true;
if ( std::toupper(*s1) > std::toupper(*s2) ) return false;
// end of both strings, exact match
if ( *s1 == 0 && *s2 == 0 ) return false;
// compare upper case vs lower case ('A' vs 'a')
if ( *s1 > *s2) return false;
if ( *s1 < *s2) return true;
++s1; ++s2;
}
});
First, get rid of the hard-coded ASCII-isms. C and C++ have long had functions for determining whether a character is a letter, a digit, uppercase, lowercase, etc. Look them up.
Second, describe clearly what goes into determining the order that you want the result to be in.
Third, from that description, write a function that takes two strings, and tells you whether the first string should come before the second. Use that function in the sort.
You can sort a vector using std::sort and get a reference to the first character in a std::string using std::string::at() :
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
char l_ch, r_ch;
l_ch = lhs.at(0);
r_ch = rhs.at(0);
return l_ch < r_ch;
});
I think it's really enough to skip exactly equal prefixes and then compare once with uppercasing:
std::vector<std::string> vec;
//
std::sort(vec.begin(), vec.end(), [](const std::string& lhs, const std::string& rhs)
{
const char* s1=lhs.c_str();
const char* s2=rhs.c_str();
while(*s1 && *s1 == *s2) {++s1; ++s2;}
int rc = toupper(*s1) - toupper(*s2);
if (rc) return rc;
return *s1 - *s2;
});
If you need to compare by first letter only, simply remove while(*s1 && *s1 == *s2) {++s1; ++s2;}
I compiled the following code, But I got a serious problem.
When I compiled this code in visual studio 2015, It works perfectly.
However When I compile this code in Dev C++, I think it doesn't print "Yes" as an answer.
For example, when I type words like,
lol
Was it a car or a cat i saw?
abcdefghiihgfedcba
these inputs must return yes, but in dev c++ returns no.
Why does this problem occur?
#include <iostream>
#include <string>
using namespace std;
bool is_palindrome(char input[], int numOfSlots);
int main(void) {
char text[256], fixed[256];
cin.getline(text, sizeof(text), '\n');
for (int i = 0; i < sizeof(text); i++) {
text[i] = toupper(text[i]);
}
int j = 0;
for (int i = 0; i < sizeof(text); i++) {
if ((text[i] >= '0' && text[i] <= '9') || (text[i] >= 'A' && text[i] <= 'Z')) {
fixed[j] = text[i];
j++;
}
}
fixed[j] = '\0';
string s_fixed = fixed;
if (is_palindrome(fixed, s_fixed.length()) == true) {
cout << "Yes";
}
else {
cout << "No";
}
return 0;
}
bool is_palindrome(char input[], int numOfSlots) {
int i = 0;
while (i < numOfSlots / 2)
{
if (input[i] != input[(numOfSlots - 1) - i])
return false;
i++;
}
return true;
}
Your program exhibits undefined behavior since you are using uninitialized data.
You have:
char text[256], fixed[256];
which are uninitialized arrays. And then you go to access them using:
for (int i = 0; i < sizeof(text); i++) {
text[i] = toupper(text[i]); // Accessing uninitialized array
}
You can fix it using couple of ways:
Initialize the arrays.
char text[256] = {0}, fixed[256] = {0};
Access only the elements that were filled in the call to getline.
size_t size = strlen(text);
for (int i = 0; i < size; i++) {
However, the better fix is to always use the second method. That way, you don't process unnecessary data.
using std::string as replacement of strlen() is rather strange, when you can use it much better way:
bool is_palindrome( const std::string &input );
int main(void) {
std::string text;
getline(cin,text);
for (size_t i = 0; i < text.length(); i++) {
text[i] = toupper(text[i]);
}
std::string fixed;
for (size_t i = 0; i < text.length(); i++) {
if ((text[i] >= '0' && text[i] <= '9') || (text[i] >= 'A' && text[i] <= 'Z')) {
fixed += text[i];
}
}
if (is_palindrome(fixed)) {
cout << "Yes";
}
else {
cout << "No";
}
return 0;
}
bool is_palindrome(const std::string &input) {
size_t numOfSlots = input.length();
int i = 0;
while (i < numOfSlots / 2)
{
if (input[i] != input[(numOfSlots - 1) - i])
return false;
i++;
}
return true;
}
of course your program can be simplified but I tried to keep it close to original to show why it is better to use std::string instead of old style char[] in C++
Here simnpliefied version using std::string and other algos from standard libraries:
#include <iostream>
#include <string>
#include <algorithm>
bool is_palindrome( std::string str )
{
if( str.empty() ) return false;
std::transform( str.begin(), str.end(), str.begin(), []( char c ) { return std::toupper( c ); } );
str.erase( std::remove_if( str.begin(), str.end(), []( char c ) { return !std::isalnum( c ); } ), str.end() );
auto len = str.length() / 2 + 1;
return std::string( str.begin(), std::next( str.begin(), len ) ) ==
std::string( str.rbegin(), std::next( str.rbegin(), len ) );
}
int main()
{
std::string text;
std::getline( std::cin, text );
std::cout << ( is_palindrome( text ) ? "yes" : "no" ) << std::endl;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
int main() {
string str_1 = "Gandalf";
string str_2 = "dal";
for (int i = 0; i <= str_1.length() - 2; i++)
for (int j = 0; j <= str_2.length(); j++) {
if (str_2[j] == str_1[i]) {
if (str_2[j + 1] == str_1[i + 1]) {
if (str_2[j + 2] == str_1[i + 2])
cout << "true";
}
}
}
return 0;
}
I can do it but if length of str_2 is 4 characters, program doesn't work.
I want that program can work for every length of string
but how?
The function find below basically reproduces the behaviour of std::string::find (without the starting position parameter). You need to:
loop through the outer string, and at each step:
loop through the second string checking each character.
If any of these fail, drop back to the outer loop.
If we make it all the way through the inner loop, then the second string is there, and return the current position in the outer loop.
If we run out of space in the first string, just skip the rest.
Hopefully the comments make this clear. I also include a little utility function to turn the found position into true/false, and some tests.
#include <iomanip>
#include <iostream>
#include <string>
std::string::size_type find(const std::string& s1,
const std::string& s2)
// return the position of s2 within s1,
// else npos if it is not present.
{
using size_type = std::string::size_type;
size_type curPos = 0;
size_type lim = s1.size();
size_type innerLim = s2.size();
for (; curPos<lim; ++curPos) { // loop through s1
if (lim < curPos+innerLim) {
break; // not enough space left
}
size_type innerPos = 0;
for(; innerPos < innerLim // loop through s2, while matching
&& curPos + innerPos < lim
&& s1[innerPos+curPos] == s2[innerPos];
++innerPos) ; // do nothing in the loop
if (innerPos == innerLim) { // matched the whole loop
return curPos;
}
}
return std::string::npos; // never matched
}
bool contains(const std::string& s1,
const std::string& s2)
{
return find(s1, s2)!=std::string::npos;
}
int main()
{
std::cout
<< std::boolalpha
<< contains("abc", "") << '\n' // true
<< contains("abc", "abc") << '\n' // true
<< contains("abc", "bc") << '\n' // true
<< contains("abc", "abcd") << '\n' // false
<< contains("abc", "abd") << '\n' // false
<< contains("abc", "xyz") << '\n';// false
}
This does more than what you really need, but it most closely models the "real" answer (use the facilities the language provides). Plus it makes it not a great homework answer, but contains all the clues to write your homework answer.
You could try something like this:
for (int i = 0; i < str_1.length() - str_2.length(); i++) {
bool is_same = true;
for (int j = 0; j < str_2.length(); j++) {
if (str[i + j] != str_2[j]) {
is_same = false;
break;
}
}
if (is_same) {
std::cout << "true" << std:endl;
}
}
It iterates over every character in str_1 and checks whether the character sequence starting at that point is the same as str_2.
/* Write a program that would mix-and-merge two given strings (s1 and s2) into string s3 as follows:
first character of s1, first character of s2, second character of s1, second character of s2, etc. */
But the problem with my code is: that if i type for s1 "John" and then for s2 "Stevens"
Result will be = JSothenv e n s.
How do i fix the spaces that are left after one of the strings ends?
The way i thought i would fix it is i would check with the ifs i have below in the for loop To see if the index is null or '\0' but that doesn't work as the string holds random values after the string ends.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1, s2;
string s3;
int i; // For index
int j = 0; // For second index and loop checking
cout << "Type first string: ";
getline(cin, s1);
cout << "Type second string: ";
getline(cin, s2);
s3.resize(s1.size() + s2.size() + 100); // The + 100 is used so we have space for all the characters. The + 100 is not needed if i fix my problem.
for(i = 0; j <= s1.size(); i += 2)
{
if(s1[j] == null) // With what do i check it?
{
break;
}
else
{
s3[i] = s1[j];
++j;
}
}
j = 0;
for(i = 1; j <= s2.size(); i += 2)
{
if(s2[j] == null)
{
break;
}
else
{
s3[i] = s2[j];
}
++j;
}
for( i = 0; i <= s3.size(); ++i)
{
cout << s3[i];
}
return 0;
Try this:
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const *argv[])
{
string s1, s2, s3;
int i,j,k;
cout << "Type first string: ";
getline(cin, s1);
cout << "Type second string: ";
getline(cin, s2);
s3.resize(s1.size()+s2.size());
for(i = 0, j = 0, k = 0; j < s1.size() && k < s2.size(); i++) {
if(i & 1) {
s3[i] = s2[k++];
} else {
s3[i] = s1[j++];
}
}
if(j == s1.size()) {
while(k < s2.size()) {
s3[i++] = s2[k++];
}
} else {
while(j < s1.size()) {
s3[i++] = s1[j++];
}
}
cout << s3 << endl;
return 0;
}
You can replace the code after getline with this:
string::const_iterator i1 = s1.begin(), i2 = s2.begin();
bool useFirst = true;
while (i1 != s1.end() || i2 != s2.end())
{
if (useFirst && i1 != s1.end())
{
s3.push_back(*i1++);
}
else if (i2 != s2.end())
{
s3.push_back(*i2++);
}
useFirst = !useFirst; // switch over for next iteration
}
cout << s3 << endl;
It's a bit simpler, and basically iterates alternately over the two strings until they're both exhausted.
It's quite simple to write a generic interleave algorithm that iterates over both ranges once:
template<typename InputIterator1, typename InputIterator2, typename OutputIterator>
OutputIterator interleave(InputIterator1 first1, InputIterator1 end1,
InputIterator2 first2, InputIterator2 end2,
OutputIterator out)
{
while(first1 != end1 && first2 != end2)
{
out = *first1;
out = *first2;
++first1;
++first2;
}
// copy remaining elements from both ranges
while(first1 != end1)
{
out = *first1;
++first1;
}
while(first2 != end2)
{
out = *first2;
++first2;
}
return out;
}
Live demo here. Usage is like std::set_merge:
interleave(begin(s1), end(s1),
begin(s2), end(s2),
std::back_inserter(result));
It iterates over both ranges until one runs out, then copies the remaining elements in both ranges. Iterates once over both containers and has one double end check I couldn't get rid of without assuming one range to be larger than the other. Can handle two different containers now