0

I am using the brute force string search algorithm to search through a small sentence, however I want the algorithm to return every time it finds the certain string instead of finding it once and then stopping

//Declare and initialise variables
string pat, text;

text = "This is a test sentence, find test within this string";

cout << text << endl;

//User input for pat
cout << "Please enter the string you want to search for" << endl;
cin >> pat;

//Set the length of the pat and text
int patLength = pat.size();
int textLength = text.size();

//Algorithm
for (int i = 0; i < textLength - patLength; ++i)
{
    //Do while loop to run through the whole text
    do
    {
        int j;

        for (j = 0; j < patLength; j++)
        {
            if (text[i + j] != pat[j])
                break; // Doesn't match here.
        }

        if (j == patLength)
        {
            finds.push(i); // Matched here.
        }
    } while (i < textLength);
}

//Print output
cout << "String: " << pat << " was found at positions: " << finds.top();

The program stores each find in a queue. When I run this program, it asks for the 'pat', then does nothing. I have done a bit of debugging and found that it is probably the do while loop. However I can't find a fix

Brady Harper
  • 235
  • 1
  • 3
  • 17
  • Are you permitted to use the algorithms associated with the std::string class? In particular, you should consider the [string::find](https://en.cppreference.com/w/cpp/string/basic_string/find) method. – pstrjds Dec 07 '18 at 14:56
  • 2
    https://ericlippert.com/2014/03/05/how-to-debug-small-programs/ – melpomene Dec 07 '18 at 14:58
  • 2
    `while (i < textLength);` you dont modify the value of `i` inside the while loop, hence once entered you never leave it – 463035818_is_not_an_ai Dec 07 '18 at 14:58
  • i dont fully understand the logic, but maybe you only need either the outer for loop **or** the while loop, but not both – 463035818_is_not_an_ai Dec 07 '18 at 14:59
  • note that `std::string::find` can do what you want, you just have to call it repeatedly. It takes a parameter to choose where to start the search, so if the last match was found at index `i` you just have to start the next one at `i+patLength` – 463035818_is_not_an_ai Dec 07 '18 at 15:07
  • How many times can you find "aa" within "aaaaa"? – Tim Randall Dec 07 '18 at 15:24
  • Just remove the do/while loop. You already have the for loop to search all the text. – stark Dec 07 '18 at 15:50

2 Answers2

0

You could use the std::string::find function combined with a function that you call for each find.

#include <iostream>
#include <functional>
#include <vector>
#include <sstream>

void Algorithm(
        const std::string& text, const std::string& pat,
        std::function<void(const std::string&,size_t)> f, std::vector<size_t>& positions)
{
    size_t pos=0;
    while((pos=text.find(pat, pos)) != std::string::npos) {
        // store the position
        positions.push_back(pos);
        // call the supplied function
        f(text, pos++);
    }
}

// function to call for each position in which the pattern is found
void gotit(const std::string& found_in, size_t pos) {
    std::cout << "Found in \"" << found_in << "\" @ " << pos << "\n";
}

int main(int argc, char* argv[]) {
    std::vector<std::string> args(argv+1, argv+argc);

    if(args.size()==0)
        args.push_back("This is a test sentence, find test within this string");

    for(const auto& text : args) {
        std::vector<size_t> found_at;
        std::cout << "Please enter the string you want to search for: ";
        std::string pat;
        std::cin >> pat;

        Algorithm(text, pat, gotit, found_at);

        std::cout << "collected positions:\n";
        for(size_t pos : found_at) {
            std::cout << pos << "\n";
        }
    }
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

My first bit of advice would be to structure your code into separate functions.

Let's say you have a function that returns the position of the pattern's first occurrence in a sequence of characters:

using position = typename std::string::const_iterator;
position first_occurrence(position text_begin, position text_end, const std::string& pattern);

If there is no more occurrence of the pattern, it returns text_end.

You can now write a very simple loop:

auto occurrence = first_occurrence(text_begin, pattern);
while (occurrence != text_end) {
    occurrences.push_back(occurrence);
    occurrence = first_occurence(occurrence + 1, text_end, pattern);
}

to accumulate all the occurrences of the pattern.

The first_occurrence function already exists in the standard library under the name of std::search. Since C++17, you can customize this function with pattern-searching specialized searchers, such as std::boyer_moore_searcher: it pre-processes the pattern to make it faster to look for in the string. Here's an example application to your problem:

#include <algorithm>
#include <string>
#include <vector>
#include <functional>

using occurrence = typename std::string::const_iterator;

std::vector<occurrence> find_occurrences(const std::string& input, const std::string& pattern) {
    auto engine = std::boyer_moore_searcher(pattern.begin(), pattern.end());
    std::vector<occurrence> occurrences;
    auto it = std::search(input.begin(), input.end(), engine);
    while (it != input.end()) {
        occurrences.push_back(it);
        it = std::search(std::next(it), input.end(), engine);
    }
    return occurrences;
}

#include <iostream>

int main() {
    std::string text = "This is a test sentence, find test within this string";
    std::string pattern = "st";
    auto occs = find_occurrences(text, pattern);
    for (auto occ: occs) std::cout << std::string(occ, std::next(occ, pattern.size())) << std::endl;
}
papagaga
  • 1,108
  • 8
  • 14