2010-11-08 3 views
3

OK, voici encore une autre question de la "Comment faire mieux en STL?" séries.nombre de correspondances dans deux séquences avec STL

Nous avons deux plages, désignées par first1, last1 et first2. Nous voulons trouver le nombre de différentes années i de [0, last1-Premier1] tel que *(first1 + i) == *(first2 + i)

Par exemple:

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

Pour ces deux gammes, la réponse est 4.

Y at-il un bon moyen STL de le faire? Je veux dire de préférence sans aucun manuel pour, tandis que etc. Merci!

+1

Je ne pense pas qu'il y ait un bon moyen de le faire sans boucles manuelles. – kennytm

+3

@Kenny: il doit y avoir. Dans d'autres langues, c'est aussi simple que «somme». zipWith (\ x y si x == y puis 1 sinon 0) ' –

+0

@Konrad:' zipWith' de C++ ('transform') nécessite un itérateur de sortie personnalisé. – kennytm

Répondre

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

MISE À JOUR

Konrad a souligné dans les commentaires ci-dessous que cela repose sur une conversion implicite légèrement infâmes de bool à int. Bien que cela soit tout à fait légal, il peut être évité ainsi:

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>); 
+2

Nice.C'est la réponse ** correcte ** –

+0

Ceci est genuis! +100 –

+0

Wow.Belle. – nakiya

0

Vous pouvez utiliser std::transform sur les deux plages avec un foncteur binaire avec état qui incrémente un compteur sur la correspondance. Tenir mon nez, car il garde son état en tant que membre statique. Vous pouvez éviter le "retour à la source" que @Johannes a soulevé en utilisant equal plutôt que transform mais l'état par static est encore en désordre ici (dans mon code de toute façon).

#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; 
}; 
+0

nous ne sommes pas techniquement autorisés foncteurs stateful sommes-nous? –

+1

Je ne pense pas que le foncteur de 'std :: transform' est autorisé à être avec état (voir http://www.drdobbs.com/184403769). En pratique, vous vous en tirerez probablement. –

+0

Le problème le plus grave est - que faites-vous avec l'itérateur de sortie? Ecrire la valeur dans l'une des séquences serait très médiocre. Ecrire un itérateur de sortie noop uniquement dans ce but serait juste bizarre. En outre, l'écriture d'un foncteur binaire avec état peut violer son exigence "sans aucun manuel pour, tandis que etc."(bien que certaines personnes aiment écrire plus que nécessaire, juste pour utiliser les algorithmes STL, et détester les boucles, mais aiment les grandes définitions de classe.) –

1

Vous pouvez abuser du std :: algorithme de décalage (et la fonction lambda):

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'; 
2

Je suis venu avec la solution suivante , insipired par fonctions zip() des langues fonctionnelles. Il compile et fonctionne bien, mais à mon humble avis, c'est probablement l'utilisation la plus dangereuse des paires d'itérateur que j'ai jamais rencontrées, donc je ne recommande pas de l'utiliser dans le code de production tel quel.

#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; 
} 
+0

vous savez c'est horrible :) –

+0

J'aime cette solution mieux que celle acceptée. Définitivement +1! – missingfaktor

+0

@missingfaktor: en fait, j'aime la solution d'Oli Charlesworth mieux que celle-ci, en ce qui concerne la résolution du problème dans la question. Cependant, cette classe 'zipper' arrive à résoudre des problèmes plus généraux! Je souhaite juste que je pourrais venir avec une version n-séquence appropriée de cela en utilisant des arguments de modèle variable et des tuples génériques de façon lisible. –

Questions connexes