简体   繁体   中英

number of matches in two sequences with STL

OK, here is yet another question of the "How to do it better in STL?" series.

We have two ranges, designated by first1, last1, and first2. We want to find the number of different i's from [0, last1-first1] such that *(first1 + i) == *(first2 + i)

For example:

{a, b, c, d, d, b, c, a}
{a, a, b, c, d, c, c, a}
 ^           ^     ^  ^ 

For these two ranges the answer is 4.

Is there a good STL way to do it? I mean preferrably without any manual for's, while's etc. Thanks!

std::inner_product(first1, last1, first2, 0, std::plus<T>(), std::equal_to<T>());

UPDATE

Konrad has pointed out in the comments below that this relies on slightly nefarious implicit conversion from bool to int . Whilst this is completely legal, it can be avoided thus:

template <typename T>
int match(const T &a, const T &b) { return (a == b) ? 1 : 0; }

std::inner_product(first1, last1, first2, 0, std::plus<T>(), match<T>);

I've come up with the following solution, insipired by functional languages' zip() functions. It compiles and runs fine, but IMHO, this is probably the most unsafe use of begin() & end() iterator pairs I've ever come across, so I don't recommend using it in production code as is.

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <iostream>
#include <vector>

namespace {

        // Iterator for pairs of items at same position in two sequences.
    template<typename Lhs, typename Rhs>
    class zipper
    {
            // Keep track of position in input sequences.
        Lhs myLhs;
        Rhs myRhs;
    public:
            // Minimal assumptions on iterator types `Lhs` and `Rhs`.
        typedef std::input_iterator_tag iterator_category;
        typedef std::pair<
            typename std::iterator_traits<Lhs>::value_type,
            typename std::iterator_traits<Rhs>::value_type
        > value_type;
        typedef value_type& reference;
        typedef value_type* pointer;
        typedef std::ptrdiff_t difference_type;

        zipper ( Lhs lhs, Rhs rhs )
            : myLhs(lhs), myRhs(rhs)
        {}
        value_type operator* () const {
            return (value_type(*myLhs, *myRhs));
        }
        bool operator== ( const zipper<Lhs,Rhs>& other ) const {
            return ((myLhs == other.myLhs) && (myRhs == other.myRhs));
        }
        bool operator!= ( const zipper<Lhs,Rhs>& other ) const {
            return ((myLhs != other.myLhs) || (myRhs != other.myRhs));
        }
        zipper<Lhs,Rhs>& operator++ () {
            ++myLhs, ++myRhs; return (*this);
        }
        zipper<Lhs,Rhs> operator++ ( int ) {
            const zipper<Lhs,Rhs> old(*this);
            ++(*this); return (old);
        }
    };

       // Shorthand "a la" std::make_pair().
    template<typename Lhs, typename Rhs>
    zipper<Lhs,Rhs> make_zipper ( Lhs lhs, Rhs rhs )
    {
        return (zipper<Lhs,Rhs>(lhs, rhs));
    }

        // Check for equal items in a pair.
    template<typename T> struct equal_pair
    {
        bool operator() ( const std::pair<T,T>& x ) const
        {
            return (x.first == x.second);
        }
    };

}

int main ( int, char ** )
{
        // Test data.
    const std::string lhs("abcddbca");
    const std::string rhs("aabcdcca");

        // Count how many pairs are equal.
    const std::size_t equal_pairs = std::count_if(
        make_zipper(lhs.begin(),rhs.begin()),
        make_zipper(lhs.end()  ,rhs.end()  ), equal_pair<char>());

        // Outputs "There are 4 equal pairs.".
    std::cout
        << "There are " << equal_pairs << " equal pairs." << std::endl;
}

You can abuse the std::mismatch algorithm (and lambda function):

const int lista[] = { 1, 2, 3, 4, 4, 2, 3, 1 };
const int listb[] = { 1, 1, 2, 3, 4, 3, 3, 1 };
int count = 0;

std::mismatch(lista, lista+8, listb, [&count](int a, int b)->bool
{
    if (a == b) ++count;
    return true;
});

std::cout << count << '\n';

You could use std::transform on the two ranges with a stateful binary functor that increments a counter on match.

Holding my nose, since this keeps state as a static member. You can avoid the 'write back to source' that @Johannes raised using equal rather than transform but statefulness via static is still messy here (in my code anyhow).

#include <vector>
#include <algorithm>

class match {
public:
    char operator()(const char& lhs, const char& rhs)
    {
        if (lhs == rhs)
        {
            ++count;
        }
        return rhs;
    }
    static size_t count;
};

size_t match::count(0);

class matchEqual {
public:
    bool operator()(const char& lhs, const char& rhs)
    {
        if (lhs == rhs)
        {
            ++count;
        }
        return false;
    }
    static size_t count;
};

size_t matchEqual::count(0);

int main()
{

    vector<char> vec1;
    vec1.push_back('a');
    vec1.push_back('b');
    vec1.push_back('c');
    vec1.push_back('d');
    vec1.push_back('d');
    vec1.push_back('b');
    vec1.push_back('c');
    vec1.push_back('a');

    vector<char> vec2;
    vec2.push_back('a');
    vec2.push_back('a');
    vec2.push_back('b');
    vec2.push_back('c');
    vec2.push_back('d');
    vec2.push_back('c');
    vec2.push_back('c');
    vec2.push_back('a');

    transform(vec1.begin(), vec1.end(), vec2.begin(), vec2.begin(), match());

    size_t matches = match::count;

    equal(vec1.begin(), vec1.end(), vec2.begin(), matchEqual());
    matches = matchEqual::count;

    return 0;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM