An alternative to my first version.
If you don't want to use streams. Then I would go with a version that does not modify the original string.
int wordCount(std::string const& words)
{
int count = 0;
bool inWord = false;
foreach(char loop: words)
{
/*
* If we are not in a word and hit a valid letter (part of word)
* Then increment the word count and mark we are in the middle of
* a word (so now the else part of this is active for subsequent
* iterations.
*/
if (!inWord) {
if (isLetter(loop)) {
++count;
inWord = true;
}
} else {
/*
* If we are in a word and hit an invalid letter (not part of word)
* Then mark that we have finished a word. This allows the
* the main part of the if to be enetered on the next iteration.
*/
if (!isLetter(loop)) {
inWord = false;
}
}
}
return count;
}
So now you need to define isLetter()
efficiently so it only applies to characters that appear in words.
bool isLetter(char v)
{
/*
* Return true for any character that is legally part of a word.
* currently this is a-z and A-Z but the question was unclear as
* to the extent of what counts as part of the word so this array
* can be adjusted as appropriate to include valid characters.
*
* Note: If this is really just a-z and A-Z then it can be replaced
* by a single call to std::isalpha() which does exactly
* the same thing.
*
* Note II: The use of the array is to trade space for time.
* This will be faster. But it also assumes we are using
* the ASCII character set.
*
* The use of the array can be traded for another technique
* this function is simply meant to define the letters that
* are valid parts of a word and any technique could be used.
*/
static bool letter[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// A-Z starts at 65
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
// a-z starts at 97
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
// Modify as appropriate.
};
return letter[static_cast<unsigned char>(v)];
}
std::size_t
for counting instead ofint
-- it expresses the intent better (non-negative size -- as opposed to an arbitrary / possibly negative integer), it is more established/idiomatic (both C and C++ std libs use it for counts), and there's more of a guarantee that it's large enough to store sizes (int
is only guaranteed to represent 16 bits, which for a signed type means values up to 32767: en.cppreference.com/w/cpp/language/types#Integer_types). \$\endgroup\$