2

I've been searching for some more information on this topic, and can't seem to find the answer I'm looking for, so I hope you can help!

Part of an assignment I'm working on is to write a program that searches an array of strings (address book), and returns matches if a full or partial match is found. I'm able to do it easily using an array of C-Strings, with the strstr() function running through a for loop and setting the pointer to the result of running the user input keyword into the array (see below).

My question is, how would I be able to do this, if at all, utilizing String objects? I also need to take into consideration there being more than one possible match. Is this the most efficient way of working this program out as well? I've already submitted my working version, I'm just curious as to some other methods to accomplish the same task!

#include <iostream>
#include <cstring>
using namespace std;

int main()
{

  bool isFound = false;         // Flag to indicate whether contact is found
  const int SIZE = 11;          // Size of contacts array
  const int MAX = 50;           // Maximum characters per row
  char contacts[SIZE][MAX] = { 
                                "Jig Sawyer, 555-1223",
                                "Michael Meyers, 555-0097",
                                "Jason Vorhees, 555-8787",
                                "Norman Bates, 555-1212",
                                "Count Dracula, 555-8878",
                                "Samara Moran, 555-0998",
                                "Hannibal Lector, 555-8712",
                                "Freddy Krueger, 555-7676",
                                "Leather Face, 555-9037",
                                "George H Bush, 555-4939",
                                "George W Bush, 555-2783"
                              };
  char *ptr = NULL;             // Pointer to search string within contacts
  char input[MAX];              // User search input string


  // Get the user input
  cout << "Please enter a contact to lookup in the address book: ";
  cin.getline(input,MAX);

  // Lookup contact(s)
  for (int i=0; i<SIZE; i++)
  {
    ptr = strstr(contacts[i], input);
    if (ptr != NULL)
      {
        cout << contacts[i] << endl;
        isFound = true;
      }
  }

  // Display error message if no matches found
  if (!contactFound)
    cout << "No contacts found." << endl;

  return 0;
} 

As you can tell, I like horror movies :)

bran.io
  • 72
  • 1
  • 9
  • `My question is, how would I be able to do this, if at all, utilizing String objects?` First ask yourself what algorithm or data structure can be used to do efficient searching. Whether you use Strings or not shouldn't come into play at this initial point. – PaulMcKenzie Apr 10 '14 at 17:09
  • I'm not sure if there's a premade function for it, but `boost::adaptors::filtered` with a short lambda makes this pretty easy. – chris Apr 10 '14 at 17:12
  • Ah, failed to mention - 2nd semester CS student. Data Structures & Algos next semester. I went with the C-String method simply because I learned some C before moving into C++. Obviously this is a very small list, but if I were to have 10K records I'd want to go the most efficient route. – bran.io Apr 10 '14 at 17:14
  • @bran.io What about keeping the records sorted by name and then a binary search? –  Apr 10 '14 at 17:14
  • @bran.io - "Data Structures & Algos next semester" - sounds like to find most efficient way to search for a needle in the haystack using bare hands. Even binary search won't do (as faranwath suggests)? – SChepurin Apr 10 '14 at 17:18
  • @faranwath, that would be an ideal next step - although I haven't gotten to sorting arrays yet (I briefly reviewed binary and linear search algos though). Assuming the contents were already sorted, would I be able to use a binary search to return multiple partial matches? (e.g. key 'or' would have 3 partial matches). The example stated array of string objects, but I guess I could have also created a struct called Contact with a string members name, phoneNum. – bran.io Apr 10 '14 at 17:27
  • @SChepurin yeah definitely. I definitely want to fast forward to the good stuff :) – bran.io Apr 10 '14 at 17:28
  • @bran.io - Then using an unsorted array and searching each item one-by-one is the most efficient way. You can have all the fancy functions and syntax in the world, you can't speed up a linear search if you have to go through the elements one-by-one. That's why honestly, there is very little (except clean up the syntax a bit) that you can do to get the code to be more efficient if you're constrained to using an unsorted array. – PaulMcKenzie Apr 10 '14 at 17:30
  • @bran.io Well, you can always count on `std::sort` to do the hard work if you don't want to mess with sorting. As for finding all the occurrences of an item, you can always find **one** (binary search will do the job) and then, given that the array is sorted, the remaining items will be in the vicinity. However, this is somehow overkill if you don't know algorithms yet. Do as PaulMcKenzie says: go with linear search. –  Apr 10 '14 at 17:33

2 Answers2

2

You really need to break each string into sortable components. If you don't know about structures yet, you can use more arrays. This would allow you to build "index" tables that would speed up the search.

The most efficient method determines on the quantity of the data and the organization of the data.

For small sets of data, the time difference between the different search methods is usually negligible -- some other item in your program will take longer (such as input or output).

With string data, most of the time is spent comparing each character of one string to another. The other operations, such as moving indices around, are negligible.

Since the comparison between search methods has already been performed, search the web for "Performance string searching comparing".

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
2

An alternative would be to use regular expressions for string search. Now there's a lot of info out there and I'll just be providing a simple example where you try to match a subrange of the records (addresses) with a word2Search (which I've hardcoded to avoid cluttering the example).

I'm also using (a technique already mentioned in the comments) a preprocessing step where the array is sorted. Beware of two things :

  • The sorting is done to enable a fast searching method, ie binary search (implemented with lower_bound upper_bound here)

  • If the word you are searching is not at the beginning of a record, there is no point in sorting the records since you won't be able to find a valid range (here it ite) to search into (eg if you search for the numbers, the sorting of the string would be done in lexicographical comparison between strings, so it wouldn't be any good for locating 555 among strings beginning with M J and so on)

Explanation in the comments:

int main()
{
    // 1. Minor change - an array of strings is used
    string contacts[] = { 
        "Jig Sawyer, 555-1223",
        "Michael Meyers, 555-0097",
        "Jason Vorhees, 555-8787",
        "Norman Bates, 555-1212",
        "Count Dracula, 555-8878",
        "Samara Moran, 555-0998",
        "Hannibal Lector, 555-8712",
        "Freddy Krueger, 555-7676",
        "Leather Face, 555-9037",
        "George H Bush, 555-4939",
        "George W Bush, 555-2783"
    };
    // 2. The array is sorted to allow for binary search
    sort(begin(contacts), end(contacts));
    // 3. Example hard coded a word to search 
    string word2Search = "George";
    // 4. A regular expression is formed out of the target word
    regex expr(word2Search);
    // 5. Upper and lower bounds are set for the search
    char f = word2Search[0];
    std::string val1(1, f);
    std::string val2(1, ++f);
    // 6. Perform the search using regular expressions
    for (auto it(lower_bound(begin(contacts), end(contacts), val1)), 
        ite(lower_bound(begin(contacts), end(contacts), val2)); it != ite; ++it)
    {
        if (regex_search(it->begin(), it->end(), expr)) {
            cout << *it << endl;
        }
    }

    return 0;
}
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • Thanks a lot Nikos, and for providing such a clear example. I was learning regex in ruby but I can certainly apply that to C++. It's fascinating seeing the way you can solve a problem in one of these newer languages fairly effortlessly, compared to all the flexibility you have with a workhorse language such as C/C++. – bran.io Apr 10 '14 at 20:31