std::search to find a rightmost subsequence - c++

I am trying to find a rightmost subsequence in a range with std::search and std::make_reverse_iterator.
However the iterator returned always point to the start of a range. What am I doing wrong?
TEST(basic_test, find_from_right)
{
std::vector<uint8_t> array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::array<uint8_t, 2> subSeq{3, 4};
auto found = std::search(std::make_reverse_iterator(array.cend()),
std::make_reverse_iterator(array.cbegin()),
subSeq.cbegin(),
subSeq.cend());
// makes no difference
// std::make_reverse_iterator(subSeq.cend()),
// std::make_reverse_iterator(subSeq.cbegin()));
auto distance = std::distance(found.base(), array.cbegin());
EXPECT_EQ(distance, 3);
}
Output:
Failure
Expected equality of these values:
distance
Which is: 0
3
I have a function that is provided with 2 template RandomIterators, so I have to call std::make_reverse_iterator. These containers are just for the sake of reproducing the problem and compiling the example.

I think this could solve your problem (if you are okay with C++17):
From https://en.cppreference.com/w/cpp/algorithm/find_end:
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
std::vector<int>::iterator result;
std::vector<int> t1{1, 2, 3};
result = std::find_end(v.begin(), v.end(), t1.begin(), t1.end());
if (result == v.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(v.begin(), result) << "\n";
}
std::vector<int> t2{4, 5, 6};
result = std::find_end(v.begin(), v.end(), t2.begin(), t2.end());
if (result == v.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(v.begin(), result) << "\n";
}
}
So in your case:
auto result = std::find_end(array.begin(), array.end(), subSeq.begin(), subSeq.end());
if (result == array.end()) {
std::cout << "sequence not found\n";
} else {
std::cout << "last occurrence is at: "
<< std::distance(array.begin(), result) << "\n";
}

Related

How to lexicographically compare two vectors in reverse order?

If I want to compare two vectors in lexicographical order, I can do as follows:
int main() {
std::vector<int> a{0, 7, 8, 9};
std::vector<int> b{1, 2, 3, 4};
std::cout << std::boolalpha;
std::cout << "a < b returns " << (a < b) << '\n';
}
But doing the same in reverse order fails to compile:
int main() {
std::vector<int> a{3, 2, 1};
std::vector<int> b{9, 8, 7, 6};
std::cout << std::boolalpha;
std::cout << "revrese a < reverse b returns " << ((a | std::views::reverse) < (b | std::views::reverse)) << '\n';
}
The latter code fails with:
<source>:23:81: error: no match for 'operator<' (operand types are 'std::ranges::reverse_view<std::ranges::ref_view<std::vector<int> > >' and 'std::ranges::reverse_view<std::ranges::ref_view<std::vector<int> > >')
23 | std::cout << "reverse a < reverse b returns " << ((a | std::views::reverse) < (b | std::views::reverse)) << '\n';
| ~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| reverse_view<[...]> reverse_view<[...]>
So, how to do achieve this properly?
I needed this because, I was creating a custom big integer class. In that, I was storing nodes in little endian order. Now, to compare if two integers are equal, first I compare their sizes. Then I would need to compare equal sized vectors in lexicographical order of their reverse view.
operator< is not defined for std::views::reverse and others. There is however a normal algorithm as normal function template for it in the standard library: std::lexicographical_compare with iterator interface and std::ranges::lexicographical_compare with range interface.
std::cout << "revrese a < reverse b returns "
<< std::ranges::lexicographical_compare(a | std::views::reverse, b | std::views::reverse)
<< '\n';
As #user17732522 said you can use std::lexicographical_compare && std::views::reverse but if you want use c++11 you can simply use reverse iterators with std::lexicographical_compare.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> a{0, 7, 8, 9};
std::vector<int> b{1, 2, 3, 4};
std::cout << std::boolalpha;
std::cout << "a < b returns "
<< std::lexicographical_compare(a.cbegin(), a.cend(), b.rbegin(),
b.rend())
<< '\n';
}

Checking for a match in each element of a 2d vector

I'm creating text based TicTacToe in C++ and need to create a function that checks for a win.
Right now, I have a vector of all the moves player X has made:
std::vector<int> x_vector = {1, 2, 3, 5, 7};
I also have a 2d vector of win conditions:
std::vector<std::vector> > wins = {{1, 2, 3}, {4, 5, 6}, {7, 8, 8}};
In this case, each element of the wins vector represents a win condition. If player X ever has a combination of inputs in their vector that includes one of the win conditions, I'm trying to get a bool function to return true.
I'm new to C++ and coding in general, so all patience is appreciated, and the simpler the solution you can help me find the better.
You can iterate through your list of known wins, checking each to see if it is a subset of the list of user's moves. The std::includes function will do this test – but note that the two 'lists' need to be sorted.
To avoid having to manually sort the list of user's moves after each input, you can use the std::set container (which is inherently sorted), instead of std::vector.
The following snippet shows a relatively simple implementation of an isWin() function using this approach, along with some rudimentary test cases:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm> // For std::includes
bool isWin(const std::set<int>& test)
{
static std::vector<std::set<int>> winlist = {
{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, // Horizontal lines
{1, 4, 7}, {2, 5, 8}, {3, 6, 9}, // Vertical lines
{1, 5, 9}, {3, 5, 7}, // Diagonal lines
};
for (auto win : winlist) {
if (std::includes(test.begin(), test.end(), win.begin(), win.end())) {
return true; // Match - Win!
}
}
return false; // Didn't get a match - no win
}
int main()
{
std::set<int> s1{ 1, 2 }; // Trivial "No win" (only 2 moves)
std::cout << "s1: " << isWin(s1) << "\n";
std::set<int> s2{ 1, 2, 3 }; // Trivial "Win" (top row)
std::cout << "s2: " << isWin(s2) << "\n";
std::set<int> s3{ 2, 4, 1, 5 }; // " No Win"
std::cout << "s3: " << isWin(s3) << "\n";
std::set<int> s4{ 5, 2, 4, 6 }; // "Win" (middle row)
std::cout << "s4: " << isWin(s4) << "\n";
std::set<int> s5{ 5, 1, 3, 6, 9 }; // "Win" (diagonal)
std::cout << "s5: " << isWin(s5) << "\n";
return 0;
}
Note that this approach may not be the best for checking wins in a Tic-Tac-Toe game; however, if your purpose is to learn about vectors, sets and looking for matching sub-sequences, it may provide a useful starting-point.
For your actual user input, you would declare and initialize an empty set and then add moves using the insert member function of the std::set container class; something like this:
int main()
{
std::set<int> user{}; // Empty Set
user.insert(5);
user.insert(2);
user.insert(4);
user.insert(6);
std::cout << "user (1): " << isWin(user) << "\n";
user.clear();
user.insert(2);
user.insert(4);
user.insert(1);
user.insert(5);
std::cout << "user (2): " << isWin(user) << "\n";
return 0;
}

myProgrammingLab Palindrome Challenge Using Recursion

I'm taking an Intro to Programming class and a good chunk of the material is drilled into our heads through myProgrammingLab. I'm having a little trouble with the concept of Recursion... It's sort of been hit or miss for me. This particular problem has me stumped. When I submit my code, it offers me
CTest1.cpp: In function 'bool isPalindrome(int*, int)':
CTest1.cpp:9: error: invalid conversion from 'int' to 'int*'
CTest1.cpp:9: error: initializing argument 1 of 'bool isPalindrome(int*, int)'"
as advice, which I can assure you is not very helpful. Lol
I think my main problem is when I get to the actual recursion. I'm aware that something's off, but.. If you could just point me in the right direction, I would very much appreciate it.
A 'array palindrome' is an array which, when its elements are reversed, remains the same (i.e., the elements of the array are same when scanned forward or backward)
Write a recursive, bool-valued function, isPalindrome, that accepts an integer -valued array , and the number of elements and returns whether the array is a palindrome.
An array is a palindrome if: the array is empty (0 elements ) or contains only one element (which therefore is the same when reversed), or the first and last elements of the array are the same, and the rest of the array (i.e., the second through next-to-last elements ) form a palindrome.
My code so far:
bool isPalindrome(int arr[], int n)
{
if (n == 0 || n == 1)
{
return true;
}
else if (arr[n-1] == isPalindrome(arr[((n-1) - n) +1 ], n))
{
return true;
}
else
{
return false;
}
}
Recursion mostly has three main components:
a stopping condition (when you reach an array size small enough to be a guaranteed palindrome (0 or 1)),
a computation step (e.g. to compare the first and last item of the array and determine whether it makes sense to continue) and
a data subset selection for the nested recursion call (e.g. an array of size n - 2, excluding the first and last characters, which we already compared and found “palindrome-worthy”).
The three components in code:
bool isPalindrome(int arr[], size_t n) {
return n < 2 || (
arr[0] == arr[n - 1] &&
isPalindrome(arr + 1, n - 2));
}
Of course you may want to test the function a bit (and do not forget to run it under valgrind as well):
#include <iostream>
int main() {
std::cout << isPalindrome((int[0]){}, 0) << std::endl;
std::cout << isPalindrome((int[1]){1}, 1) << std::endl;
std::cout << isPalindrome((int[2]){1, 1}, 2) << std::endl;
std::cout << isPalindrome((int[2]){2, 1}, 2) << std::endl;
std::cout << isPalindrome((int[2]){1, 2}, 2) << std::endl;
std::cout << isPalindrome((int[3]){1, 2, 1}, 3) << std::endl;
std::cout << isPalindrome((int[3]){2, 2, 2}, 3) << std::endl;
std::cout << isPalindrome((int[3]){2, 2, 1}, 3) << std::endl;
std::cout << isPalindrome((int[4]){1, 2, 1, 2}, 4) << std::endl;
std::cout << isPalindrome((int[4]){1, 2, 2, 1}, 4) << std::endl;
std::cout << isPalindrome((int[4]){1, 2, 3, 2}, 4) << std::endl;
std::cout << isPalindrome((int[4]){2, 3, 2, 1}, 4) << std::endl;
std::cout << isPalindrome((int[4]){1, 3, 3, 1}, 4) << std::endl;
}
As a side note, this^^^ deadly struggle with arrays suggests that a different data type would be a much better choice. For example, std::string or std::vector can be initialized way easier, should be passed by reference and, as a bonus, STL containers carry size information with them. Additionally, you can use std::string_view for substrings and std::span for “subvectors” in your recursion, without copying the container over and over on each recursion level.
Here’s an example with std::string_view and three different implementations (one with recursion and two without recursion):
#include <iostream>
#include <string_view>
bool isPalindrome1(const std::string_view s) {
return s.size() < 2 || (
s[0] == s[s.size() - 1] &&
isPalindrome1(s.substr(1, s.size() - 2)));
}
bool isPalindrome2(const std::string_view s) {
const size_t end = s.size() / 2;
for (size_t i = 0; i < end; ++i)
if (s[i] != s[s.size() - i - 1])
return false;
return true;
}
bool isPalindrome3(const std::string_view s) {
auto b = s.begin();
const auto end = b + s.size() / 2;
auto e = s.rbegin();
for (; b < end; ++b, ++e)
if (*b != *e) return false;
return true;
}
int main() {
for (auto isPalindrome : {isPalindrome1,
isPalindrome2,
isPalindrome3}) {
std::cout << isPalindrome("") << std::endl;
std::cout << isPalindrome("a") << std::endl;
std::cout << isPalindrome("ab") << std::endl;
std::cout << isPalindrome("aa") << std::endl;
std::cout << isPalindrome("abc") << std::endl;
std::cout << isPalindrome("aba") << std::endl;
std::cout << isPalindrome("baab") << std::endl;
std::cout << isPalindrome("baba") << std::endl;
}
}
isPalindrome does not accept an int as a first argument. It accepts only an array, by doing this: arr[((n-1) - n) +1] you are feeeding it an int instead if an array of ints. This ((n-1) - n) +1 will evaluate to a “position” in the array, eg: arr[0] being the first element, your case an int.

Slicing a vector in C++

Is there an equivalent of list slicing [1:] from Python in C++ with vectors? I simply want to get all but the first element from a vector.
Python's list slicing operator:
list1 = [1, 2, 3]
list2 = list1[1:]
print(list2) # [2, 3]
C++ Desired result:
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2;
v2 = v1[1:];
std::cout << v2 << std::endl; //{2, 3}
This can easily be done using std::vector's copy constructor:
v2 = std::vector<int>(v1.begin() + 1, v1.end());
In C++20 it is pretty easy:
#include <span>
#include <vector>
#include <iostream>
template<int left = 0, int right = 0, typename T>
constexpr auto slice(T&& container)
{
if constexpr (right > 0)
{
return std::span(begin(std::forward<T>(container))+left, begin(std::forward<T>(container))+right);
}
else
{
return std::span(begin(std::forward<T>(container))+left, end(std::forward<T>(container))+right);
}
}
int main()
{
std::vector v{1,2,3,4,5,6,7,8,9};
std::cout << "-------------------" << std::endl;
auto v0 = slice<1,0>(v);
for (auto i : v0)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v1 = slice<0,-1>(v);
for (auto i : v1)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v2 = slice<1,3>(v);
for (auto i : v2)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v3 = slice<1,-1>(v);
for (auto i : v3)
{
std::cout << i << std::endl;
}
std::cout << "-------------------" << std::endl;
auto v4 = slice<3,3>(v);
for (auto i : v4)
{
std::cout << i << std::endl;
}
}
Result:
Program returned: 0
-------------------
2
3
4
5
6
7
8
9
-------------------
1
2
3
4
5
6
7
8
-------------------
2
3
-------------------
2
3
4
5
6
7
8
-------------------
You can also add boundary checks and other cases like negative left indices etc... but this is only an example.
Run in compiler explorer: https://godbolt.org/z/qeaxvjdbj
I know it's late but have a look at valarray and its slices. If you are using a vector of some sort of NumericType, then it's worth giving it a try.
It depends on whether you want a view or a copy.
Python's slicing for lists copies references to the elements, so it cannot be simply regarded as a view or a copy. For example,
list1 = [1, 2, 3]
list2 = list1[1:]
list2[1] = 5
print(list1) # does not change, still [1, 2, 3]
list1 = [1, 2, [3]]
list2 = list1[1:]
list2[1][0] = 5
print(list1) # changes, becomes [1, 2, [5]]
See this post for details.
DimChtz's anwer models the copy situation. If you just want a view, in C++20, you can use ranges (besides std::views::drop, std::views::take and std::views::counted are also useful):
auto v2 = v1 | std::views::drop(1); // #include <ranges>
for (auto &e: v2) std::cout << e << '\n';
or std::span:
std::span v2{v1.begin() + 1, v1.end()}; // #include <span>
for (auto &e: v2) std::cout << e << '\n';
You can follow the above answer. It's always better to know multiple ways.
int main
{
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2{v1};
v2.erase( v2.begin() );
return 0;
}
It seems that the cheapest way is to use pointer to the starting element and the number of elements in the slice. It would not be a real vector but it will be good enough for many uses.

Lower bound error

I am following this tutorial on Lower_bound() in C++. I have made a simple code to find a number in a vector lesser than or equal to a number from the vector + any number that I want. My code goes like this
cout << *lower_bound(A.begin(), A.end(), A[x] + 3);
where the vector A[] is sorted. But the code points it to a number greater than the sum of both the numbers.
For example if my vector has values as 0, 3, 5, 8, 12 and I want it to print the nearest number lesser than or equal to A[3] + 3 = 11 it should give output as 8 but it gives the output of 12. Any reasons?
Here is my code:
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> A = {0, 5, 3, 12, 8};
sort(A.begin(), A.end());
cout << "A[3] = " << A[3] << endl;
cout << *lower_bound(A.begin(), A.end(), A[3] + 3) << endl;
return 0;
}
lower_bound
Returns an iterator pointing to the first element in the range [first,last) which does not compare less than val.
In your case it is not returning the last value less than 11. It returns the first value not less than 11, which in your example is 12.
If you want the largest number not greater than your target, you can use std::greater<> and reverse iterators (or sort by std::greater<>)
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> A = {0, 5, 3, 12, 8};
std::sort(A.begin(), A.end());
std::cout << "A[3] = " << A[3] << std::endl;
std::cout << *std::lower_bound(A.rbegin(), A.rend(), A[3] + 3, std::greater<int>{}) << std::endl;
return 0;
}