A small portability bug: std::size_t
is in the std
namespace, assuming it's declared by including <cstddef>
(recommended).
No unit tests are included, but I'd expect one that tests that the result is zero when the input collection is empty. We need to initialize maxlen
to zero for that test to pass.
When comparing consecutive elements of a collection, always consider using std::adjacent_find()
. With a suitable predicate function, we can find changes from negative to non-negative and vice versa without needing to code our own loop or do any indexing.
(More advanced) Consider making your algorithm generic, templated on an iterator type, so that it can be applied to any collection (or even to an input stream directly).
Here's a version that applies all of these suggestions (and some from other answers that I've not repeated above):
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <iterator>
template<typename ForwardIt>
std::size_t getLongestSameSignSequenceLength(ForwardIt first, ForwardIt last)
{
auto const signdiff =
[](auto a, auto b){ return std::signbit(a) != std::signbit(b); };
std::size_t maxlen = 0;
while (first != last) {
ForwardIt change = std::adjacent_find(first, last, signdiff);
if (change != last) { ++change; }
std::size_t len = std::distance(first, change);
if (len > maxlen) { maxlen = len; }
first = change;
}
return maxlen;
}
// tests:
#include <vector>
int main()
{
struct testcase { std::size_t expected; std::vector<int> inputs; };
std::vector<testcase> tests
{
{0, {}},
{1, {1}},
{1, {1, -2}},
{1, {1, -2, 3}},
{1, {-1, 2, -3}},
{2, {1, 2}},
{2, {1, 2, -3}},
{2, {-1, -2, 3}},
{2, {-1, 2, 3}},
{2, {-1, 2, 3, -4}},
};
int failures = 0;
for (auto const& [e, v]: tests) {
failures += getLongestSameSignSequenceLength(v.begin(), v.end()) != e;
}
return failures;
}
get
prefix, but after that, it gets more difficult. My best effort ismaxSameSignRunLength()
but that's still not very concise... \$\endgroup\$