0

I have two arrays/vectors of strings for example

str1_arr = ["hello","my","dear","world"]
str2_arr = ["dear","my","world","hello"]

the position/sequence of the words aren't the same. I would like to know if each element in str1_arr is present in str2_arr not really caring its position inside the container.

Came across few discussion here and here but the answers are way back in 2011. Given that c++ has progressed some much , wondering if there is a better way of doing it using std::algorithms

Thanks

BhanuKiran
  • 2,631
  • 3
  • 20
  • 36
  • 2
    Please be a bit more specific, your code is not valid syntax, and the solution depends on what is the type of the container – 463035818_is_not_an_ai Oct 24 '19 at 09:28
  • 2
    Maybe [std::is_permutation](https://en.cppreference.com/w/cpp/algorithm/is_permutation) is what you need. – Lukas-T Oct 24 '19 at 09:29
  • @churill You should make an answer out of that comment. – eike Oct 24 '19 at 09:41
  • std::is_permutation would return true if the first container is in the second but the later would contain more elements than in the first. – Superlokkus Oct 24 '19 at 09:45
  • 2
    @Superlokkus why should this be? I can't reproduce this behaviour. – Lukas-T Oct 24 '19 at 09:52
  • @churill http://cpp.sh/7vu5z – Superlokkus Oct 24 '19 at 10:21
  • Possible duplicate of [C++: Comparing two vectors](https://stackoverflow.com/questions/6248044/c-comparing-two-vectors) – Superlokkus Oct 24 '19 at 10:26
  • @Superlokkus Yes I saw it afterwards ^^ But its strange, because using the version without the last second iterator, it returns `true` as expected. – Fareanor Oct 24 '19 at 10:35
  • @Fareanor That only logical since that overload would assume the second range is at least as long as the first. But move the additional element to the begining of the second contanier, and it should fail again. – Superlokkus Oct 24 '19 at 10:39
  • 1
    @Superlokkus Right :) – Fareanor Oct 24 '19 at 10:42
  • `#include #include #include std::vector str1_arr = { "hello", "my", "dear", "world" }; std::vector str2_arr = { "dear", "my", "world", "hello" }; int main() { bool isEqual = std::is_permutation(str1_arr.begin(), str1_arr.end(), str2_arr.begin()); // true std::cin.get(); } ` – abdulrhmanOmran Oct 24 '19 at 13:34
  • You should elaborate more when the solution should return true. There are a lot wrong answers, at least wrong to my understanding of "I would like to know if each element in str1_arr is present in str2_arr ", as some people seem to only look on your example ranges, and think of permutation, but what should happen, if the second range has addtional elements not in the first, but still contains all elements of the first one (here permutation would return false but that is IMHO wrong to your requirement "if each element in str1_arr is present in str2_arr" (return true)) – Superlokkus Oct 25 '19 at 09:42

2 Answers2

1

Update

Well here is a non modifying and a modifying version. Al tough sorting and copying seems like a lot of work, the highest complexity, which is of sorting is log(n) * n, and includes is just 4 * n, plus some linear ns, which is a lot less than n^2 . (Approximating the size/distance of both ranges with n, which is simply the size of the bigger one)

So for the big O notation, this solution is in O(n *log(n)) instead of O(n^2) of the naive for of for solution or std::is_permutation (which is also wrong in the results).

But I wondered, it still a pretty high constant factor of the complexity so I calculated:

Even the worst case, which would have 2 n from copying, 2(log(n) * n) from sorting and the 2(2n) from includes, is less than the n^2, of a naive solution, for a container only of the size of 14 elements.

#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
#include <iterator>


template<typename Iterator1, typename Iterator2>
bool is_included_general_modifying(Iterator1 begin1, Iterator1 end1, Iterator2 begin2, Iterator2 end2) {
    std::sort(begin1, end1);
    std::sort(begin2, end2);
    return std::includes(begin2, end2, begin1, end1);
}

template<typename Iterator1, typename Iterator2>
bool is_included_general(Iterator1 begin1, Iterator1 end1, Iterator2 begin2, Iterator2 end2) {
    const auto first_range_is_sorted = std::is_sorted(begin1, end1);
    const auto second_range_is_sorted = std::is_sorted(begin2, end2);
    if (first_range_is_sorted && second_range_is_sorted) {
        return std::includes(begin2, end2, begin1, end1);
    } else if (first_range_is_sorted) {
        auto second_range_copy = std::vector<typename std::iterator_traits<Iterator2>::value_type>(begin2, end2);
        auto new_begin2 = second_range_copy.begin(), new_end2 = second_range_copy.end();
        std::sort(new_begin2, new_end2);
        return std::includes(new_begin2, new_end2, begin1, end1);
    } else if (second_range_is_sorted) {
        auto first_range_copy = std::vector<typename std::iterator_traits<Iterator1>::value_type>(begin1, end1);
        auto new_begin1 = first_range_copy.begin(), new_end1 = first_range_copy.end();
        std::sort(new_begin1, new_end1);
        return std::includes(begin2, end2, new_begin1, new_end1);
    }
    auto first_range_copy = std::vector<typename std::iterator_traits<Iterator1>::value_type>(begin1, end1);
    auto new_begin1 = first_range_copy.begin(), new_end1 = first_range_copy.end();
    std::sort(new_begin1, new_end1);
    auto second_range_copy = std::vector<typename std::iterator_traits<Iterator2>::value_type>(begin2, end2);
    auto new_begin2 = second_range_copy.begin(), new_end2 = second_range_copy.end();
    std::sort(new_begin2, new_end2);

    return std::includes(new_begin2, new_end2, new_begin1, new_end1);
}

int main() {
    std::array<std::string, 4> str1_arr = {"hello", "my", "dear", "world"};
    std::vector<std::string> str2_arr = {"additional element", "dear", "my", "world", "hello"};
    std::cout << is_included_general(str1_arr.begin(), str1_arr.end(), str2_arr.begin(), str2_arr.end()) << "\n";
}
Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • Yes, I am looking for something that doesn't modify the range. I can write my own 'for loop inside a for loop' passing through each element from each container. But i am looking for something elegant and computationally effective. – BhanuKiran Oct 24 '19 at 19:08
  • @BhanuKiran See now – Superlokkus Oct 25 '19 at 09:37
0
std::vector<std::string> str1_arr{"hello", "my", "dear", "world"};
std::vector<std::string> str2_arr{"dear", "my", "world", "hello"};

assert(std::is_permutation(str1_arr.begin(), str1_arr.end(), 
                           str2_arr.begin(), str2_arr.end()));

According to C++ reference, std::is_permutation(first1, last1, first2, last2) returns true if there exists a permutation of the elements in the range [first1, last1) that makes that range equal to the range [first2, last2).

Evg
  • 25,259
  • 5
  • 41
  • 83
  • ` std::vector str1_arr{"hello", "my", "dear", "world"}; std::vector str2_arr{"dear", "my", "world", "hello", "AdditionaElement"};` is_permutation returns false http://cpp.sh/7vu5z – Superlokkus Oct 24 '19 at 10:20