Best method for finding and replacing a subarray - c++

I've written some code to find a desired sub-array within a larger array and replace it with a different sub-array of the same length.
e.g.:
int array[] = {1,2,3,4,1,2,3,4};
int find[] = {1,2,3};
int replace[] = {7,8,9};
replaceSubArray(array, 8, find, replace, 3);
And replaceSubArray modifies 'array' to contain {7,8,9,4,7,8,9,4}
My function looks like this:
void replaceSubArray(char* longArray, int longLength, char* find, char* replace, int shortLength) {
int findIndex = 0, replaceIndex = 0;
for (int i = 0; i < longLength; ++i) //loop through larger array
{
if (longArray[i] == find[findIndex]) //if we find a match for an element
{
if (++findIndex == shortLength) //increment the findIndex and see if the entire subarray has been found in the larger array
{
for (int j = i - (shortLength - 1); j <= i; ++j) //entire array has been matched so go back to start of the appearance of subarray in larger array
{
longArray[j] = replace[replaceIndex]; //replace the matched subarray with the contents of replace[]
replaceIndex++;
}
replaceIndex = 0; //reset replaceIndex and findIndex to 0 so we can restart the search for more subarray matches
findIndex = 0;
}
} else { //if an element wasn't matched, reset findIndex to 0 to restart the search for subarray matches
findIndex = 0;
}
replaceIndex = 0;
}
}
It works fine but I am a beginner programmer and was curious if there is any better way to do this? Or if there are any built in functions that would help.

Use standard algorithms. You have
int array[] = {1,2,3,4,1,2,3,4};
int find[] = {1,2,3};
int replace[] = {7,8,9};
then you can use (requires #include <algorithm>, #include <iterator>)
using std::begin, std::end;
auto it = begin(array);
for (;;) {
it = std::search(it, end(array), begin(find), end(find));
if (it == end(array))
break;
it = std::copy(begin(replace), end(replace), it);
}
(live demo)
You can also use the Boyer-Moore searcher: (requires #include <functional>)
using std::begin, std::end;
auto searcher = std::boyer_moore_searcher(begin(find), end(find));
auto it = begin(array);
for (;;) {
it = std::search(it, end(array), searcher);
if (it == end(array))
break;
it = std::copy(begin(replace), end(replace), it);
}
(live demo)
Whether or not this will improve performance depends on a lot of factors, so profile.

To replace just the first occurence:
#include <string.h>
void replaceSubArray(int* longArray, int longLength, int* find, int* replace, int shortLength)
{
int i, k = 0;
for (i = 0 ; i < longLength ; ++i)
{
if (longArray[i] == find[k++])
{
if ( k == shortLength )
{
memcpy(longArray + i + 1 - k, replace, sizeof(int) * shortLength);
break;
}
continue;
}
k = 0;
}
}
To replace all occurences:
#include <string.h>
void replaceSubArray(int* longArray, int longLength, int* find, int* replace, int shortLength)
{
int i, k = 0;
for (i = 0 ; i < longLength ; ++i)
{
if (longArray[i] == find[k++])
{
if ( k == shortLength )
memcpy(longArray + i + 1 - k, replace, sizeof(int) * shortLength);
else
continue;
}
k = 0;
}
}
In C I would prefer this way.
PS: The question was tagged with C too before. Noticed that just now that C tag has been removed. Still posted in case if it helps.

If the elements in your find-array are all different, you could in most cases skip some indexes in your else-case:
replace:
else { //if an element wasn't matched, reset findIndex to 0 to restart the search for subarray matches
findIndex = 0;
}
with
else { //if an element wasn't matched, reset findIndex to 0 to restart the search for subarray matches
findIndex = 0;
i+=find.length-findIndex; // there could not be a match starting before this index.
}
If not all entries in your find-index are different you could use a similar (more complicated) approach. See Knuth–Morris–Pratt algorithm
Using memcpy instead of a loop to make the actual replace should also speed things up a bit.
Hint:
Always profile each change to see if, and in which extend, the change improved the performance.

Here is the sample code in which I used std::vector and few already present feature of c++
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
int main () {
std::vector<int> vect1 = {1,2,3,4,5};
std::vector<int> find = {3,4,5};
std::vector<int> replace = {5,6,7};
auto it = std::search(vect1.begin(),vect1.end(),find.begin(),find.end()); // Finds sub array in main vect1
int i = 0;
while ((it != vect1.end()) && (i< replace.size())) {
*it = replace[i]; // replace each elements on by one start from searched index from std::search
i++; //iterate replace vector
it++; //iterate main vector
}
return 0;
}

Related

Problems with vectors, how to remove the arrays in my vectors?

I have created a function that creates all the possible solutions for a game that I am creating... Maybe some of you know the bullcow game.
First I created a function that creates a combination of numbers of max four integers and the combination can't have any repeating number in it... like...
'1234' is a solution but not '1223' because the '2' is repeating in the number. In total there is 5040 numbers between '0123' and '9999' that haven't repeating numbers.
Here is my function:
std::vector <std::array<unsigned, 4>> HittaAllaLosningar(){
std::vector <std::array<unsigned, 4>> Losningar;
for (unsigned i = 0; i < 10; i++) {
for (unsigned j = 0; j < 10; j++) {
for (unsigned k = 0; k < 10; k++) {
for (unsigned l = 0; l < 10; l++) {
if (i != j && i != k && i != l && j != k && j != l && k != l) {
Losningar.push_back({i,j,k,l});
}
}
}
}
}
return Losningar;
}
Now let's say I have the number '1234' and that is not the solution I am trying to find, I want to remove the solution '1234' from the array since that isn't a solution... how do I do that? have been trying to find for hours and can't find it. I have tried vector.erase but I get errors about unsigned and stuff... also its worth to mention the guesses are in strings.
What I am trying to do is, to take a string that I get from my program and if it isn't a solution I want to remove it from the vector if it exists in the vector.
Here is the code that creates the guess:
std::string Gissning(){
int random = RandomGen();
int a = 0;
int b = 0;
int c = 0;
int d = 0;
for (unsigned i = random-1; i < random; i++) {
for (unsigned j = 0; j < 4; j++) {
if (j == 0) {
a = v[i][j];
}
if (j == 1) {
b = v[i][j];
}
if (j == 2) {
c = v[i][j];
}
if (j == 3) {
d = v[i][j];
}
}
std::cout << std::endl;
AntalTry++;
}
std::ostringstream test;
test << a << b << c << d;
funka = test.str();
return funka;
}
The randomgen function is just a function so I can get a random number and then I go in the loop so I can take the element of the vector and then I get the integers of the array.
Thank you very much for taking your time to help me, I am very grateful!
You need to find the position of the element to erase.
std::array<unsigned, 4> needle{1, 2, 3, 4};
auto it = std::find(Losningar.begin(), Losningar.end(), needle);
if (it != Losningar.end()) { Losningar.erase(it); }
If you want to remove all the values that match, or you don't like checking against end, you can use std::remove and the two iterator overload of erase. This is known as the "erase-remove" idiom.
std::array<unsigned, 4> needle{1, 2, 3, 4};
Losningar.erase(std::remove(Losningar.begin(), Losningar.end(), needle), Losningar.end());
To erase from a vector you just need to use erase and give it an iterator, like so:
std::vector<std::array<unsigned, 4>> vec;
vec.push_back({1,2,3,4});
vec.push_back({4,3,2,1});
auto it = vec.begin(); //Get an iterator to first elements
it++; //Increment iterator, it now points at second element
it = vec.erase(it); // This erases the {4,3,2,1} array
After you erase the element, it is invalid because the element it was pointing to has been deleted. Ti continue to use the iterator you can take the return value from the erase function, a valid iterator to the next element after the one erased, in this the case end iterator.
It is however not very efficient to remove elements in the middle of a vector, due to how it works internally. If it's not important in what order the different solution are stored, a small trick can simplify and make your code faster. Let's say we have this.
std::vector<std::array<unsigned, 4>> vec;
vec.push_back({1,2,3,4});
vec.push_back({4,3,2,1});
vec.push_back({3,2,1,4});
To remove the middle one we then do
vec[1] = vec.back(); // Replace the value we want to delete
// with the value in the last element of the vector.
vec.pop_back(); //Remove the last element
This is quite simple if you have ready other functions:
using TestNumber = std::array<unsigned, 4>;
struct TestResult {
int bulls;
int cows;
}
// function which is used to calculate bulls and cows for given secred and guess
TestResult TestSecretGuess(const TestNumber& secret,
const TestNumber& guess)
{
// do it your self
… … …
return result;
}
void RemoveNotMatchingSolutions(const TestNumber& guess, TestResult result)
{
auto iter =
std::remove_if(possibleSolutions.begin(),
possibleSolutions.end(),
[&guess, result](const TestNumber& possibility)
{
return result == TestSecretGuess(possibility, guess);
});
possibleSolutions.erase(iter, possibleSolutions.end());
}
Disclaimer: it is possible to improve performance (you do not care about order of elements).

Increasing Sequence C++

I try to solve this challenge on CodeFights, but, it doesn't work. My best solution got 25/26 (time limit exceeded on the last test) but I deleted that because I tried it yesterday (it was O(n^2)). Now I tried a new one in O(n). I am very tired and I really want to get this done today, so please help me.
Here are the statements:
Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Example
For sequence = [1, 3, 2, 1], the output should be
almostIncreasingSequence(sequence) = false;
There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
almostIncreasingSequence(sequence) = true.
You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3].
And here is my code until now... (poor code):
#include <iostream>
#include <vector>
#include <algorithm>
bool almostIncreasingSequence(std::vector<int> sequence)
{
int count = 0;
for(int i = 0; i < sequence.size()-1; i++)
{
if(sequence[i] > sequence[i+1])
{
count++;
sequence.erase(sequence.begin(), sequence.begin() + i);
i--;
}
if(count == 2)
return false;
}
return true;
}
int main()
{
std::cout << std::endl;
return 0;
}
Here is a C++11 solution with O(N) runtime:
constexpr auto Max = std::numeric_limits<std::size_t>::max();
bool is_sorted_but_skip(const std::vector<int>& vec, std::size_t index = Max){
auto Start = index == 0 ? 1 : 0;
auto prev = vec[Start];
for(std::size_t i = Start + 1; i < vec.size(); i++){
if(i == index) continue;
if(prev >= vec[i]) return false;
prev = vec[i];
}
return true;
}
bool almostIncreasingSequence(std::vector<int> v)
{
auto iter = std::adjacent_find(v.begin(), v.end(), [](int L, int R){ return L >= R; });
if(is_sorted_but_skip(v, std::distance(v.begin(), iter)))
return true;
return is_sorted_but_skip(v, std::distance(v.begin(), std::next(iter)));
}
We use std::adjacent_find to find the first element, iter greater than or equal its next element. Then we check that sequence is strictly sorted while skipping iter's position.
Otherwise, we check that the sequence is strictly sorted while we skip iter+1's position
Worse case complexity: 3 linear scan
Demo
Here's a hint (well, almost a solution really):
If you see a decrease between one element to the next, then you have to remove one of them (*).
Now, what if you find two decreases, between two disjoint pairs of elements? That's right :-)
Keeping that in mind, you should be able to solve your problem using a linear scan and a bit of constant-time work.
(*) excluding the first and the last pair of elements.
This is still O(N^2), because you delete the first element of the vector in each iteration. Don't delete the first element and don't i-- in the loop.
If you must erase the numbers (you don't, but still), at least do it from the end of the list. That way erasing a number is probably an O(1) operation (I'm not 100% sure that's how std::vector is implemented).
You really don't have to erase the numbers.
#include<iostream>
#include<vector>
using namespace std;
int almostIncreasingSequence( vector<int> sequence );
int main(){
int array[] = {40, 50, 60, 10, 20, 30};
std::vector<int> vect (array, array + sizeof(array) / sizeof(int) );
bool ret = almostIncreasingSequence(vect);
if( ret ){
std::cout<<"Array is strictly increasing.";
}
else{
std::cout<<"Array is not strictly increasing.";
}
return 0;
}
bool almostIncreasingSequence(std::vector<int> sequence) {
int val = 0;
int currentBig = sequence.at(0);
for (int i = 1; i < sequence.size(); i++){
if( currentBig < sequence.at(i))
{
currentBig = sequence.at(i);
}
else{
val++;
if( val>1)
{
return false;
}
if( i > 1 ){
if (sequence.at(i) > sequence.at(i-2)){
if( currentBig < sequence.at(i) ){
}
else{
currentBig = sequence.at(i);
}
}
}
else{
currentBig = sequence.at(i);
}
}
}
return true;
}

How to find a char array in another one in c++?

I want to write a code to find a char array in another one and print out place(element) of first occur of first array. I wrote my own code and it works. But it "seems" kind of messy. Does anyone have better idea to rewrite this code?
"only strlen() is allowed. I need to write it by designing loops."
Here's the code
#include <iostream>
#include <string.h>
using namespace std;
const int len = 100;
int main() {
int i, j, k, l;
char a[len]="leonardo" , b[len]="nar";
bool es1 = false, es2=false;
i = 0;
while(i < len && !es1)
{
j = 0;
if(a[i] == b[j])
{
k = i+1;
l = j;
while (k < i+strlen(b) && !es2)
{
j = j+1;
if (a[k] == b[j]) k = k+1;
else es2 = true;
}
if (a[i+strlen(b)-1]==b[l+2] && !es2) es1 = true;
else i = i+1;
}
else i= i+1;
}
cout << endl << "element: " << i;
return 0;
}
By the way this not a real homework. I just make myself ready for a programming exam.
I just find out that the code doesn't work fine if array b[] is shorter than 3 elements. So it seems the code needs major review!
The easy way to do it would be to use std::search:
auto it = std::search(a, a + 8, b, b + 3));
Here, it points to the beginning of the found sequence in a, or to std::end(a) if not found.
This looks like substring search algorithm. You can use any of the recognized algorithms like KMP.
Can you use a string instead of a char array? If so, you can use string::find.
http://www.cplusplus.com/reference/string/string/find/
bool bFound = strA.find(strB)

Iterate over full array starting at any position, using only one variable

Fairly simple, I want to loop over every index of an array of arraysize using only one var for the loop. I have a way to do it with two vars i and j, where i tracks the actual index and loops around and j counts up to arraysize and terminates the loop:
for (unsigned int i = start, j = 0; //start is the starting index
j < arraysize;
++i == arraysize ? i = 0 : 0, ++j)
{
//do stuff
}
Is there some nifty way to do this with only i? Order doesn't matter, if backward iteration makes sense for some reason.
Clarification: I want to loop from start to arraysize - 1, then from 0 to start - 1.
At least as I understand it, you want to loop through the entire array, but you want to start from somewhere other than the beginning, then when you reach the end, you want to start back at the beginning and keep going until you reach the original starting point.
Assuming that's correct, it's pretty easy:
for (size_t i=0; i<arraysize; i++)
process(array[(i+start)%arraysize]);
I would prefer to abstract that algorithm into generic function (which would work even on things like std::forward_list), without doing superfluous modulo and addition operations (though, they may be acceptable in many cases):
#include <algorithm>
#include <iostream>
#include <iterator>
template<typename FwdIter, typename F>
F for_each_shifted(FwdIter first, FwdIter start, FwdIter last, F f)
{
using std::for_each;
return for_each(first, start, for_each(start, last, f));
}
int main()
{
using namespace std;
int v[] = { 1, 1, 2, 6, 24 };
for_each_shifted(begin(v), begin(v) + 3, end(v), [](int x)
{
cout << x << endl;
});
}
Output is:
6
24
1
1
2
Live Demo
for ( i=start; i<start+arraysize; i++ ) {
// do stuff with (i % arraysize) in place of i
}
for (size_t i = start; (i + 1) % arraysize != start: i = (i + 1) % arraysize) {
// stuff
}
This would get you there:
for (unsigned int i = start; i < start + arraySize; i++)
{
DoSomething(array[i % arraySize]);
}
Alternatively:
for (unsigned int i = 0; i < arraySize; i++)
{
DoSomething(array[(i + start) % arraySize]);
}
For example you can use the folloing loop statement
for ( int i = start; i < arraysize + start; i++ )
and inside the loop body instead of i use expression i % arraysize

Efficient circular list

I want a simple yet efficient circular buffer/queue. If I use std::vector, I have to do this:
if ( v.size() >= limit ) {
std::vector<int> it = v.begin();
v.insert( it, data );
v.erase( it+1 );
}
Is there any simpler solution?
You want to maintain the size of the buffer, overwriting older items. Just overwrite the old ones as time goes on. If you want to deal with the case where nItems < limit, then you would need to deal with that, this is just a simple example of using modulo to insert into a fixed size buffer.
std::vector<int> data(10);
for (int i = 0 ; i < 100 ; ++i)
{
data[i%10] = i;
}
for (std::vector<int>::const_iterator it = data.begin() ; it !=data.end(); ++it)
{
std::cout << *it << std::endl;
}
That method of insertion will keep the last 10 elements in the buffer.
A std::list might be an easier alternative to building a list than std::vector. There's also std::queue.
It's also funny that you're using a vector to implement a circular queue but ask a question on how to implement a circular list. Why not use a map?
In c++11 for a fixed size alternative you should be using std::array:
const unsigned int BUFFER_SIZE = 10;
std::array<int, BUFFER_SIZE> buffer; // The buffer size is fixed at compile time.
for (i = 0; i < 100; ++i) {
buffer[i % BUFFER_SIZE] = i;
}
Try std::deque. The interface is like using a std::vector but insert and removal at beginning and end are more efficient.
You can use your vectors as usual, and then create a get_element(index) function to make it feel circular. It's pretty fast and straight-forward, since it's just integer manipulation.
template<typename T>
T get_element(std::vector<T> vec, int index) {
int vector_size = vec.size();
int vector_max = vector_size - 1;
int vector_min = 0;
int index_diff = 0;
int refined_index = 0;
// index_diff is the amount of index-out-of-range. Positive means index was
// bigger than the vector size, negative means index was smaller than 0
if (index > vector_max) {
index_diff = index - vector_max;
} else if (index < vector_min) {
index_diff = index;
} else {
index_diff = 0;
}
// Make the indexing feel circular
// index mod 16 yields a number from 0 to 15
if (index_diff > 0) {
refined_index = index % vector_size;
} else if (index_diff < 0) {
int temp_index = index % vector_size;
if (temp_index != 0) {
refined_index = vector_size - std::abs(temp_index);
// if the negative mod equals to 0, we can't have 16 - 0 = 16 index,
// so we set it to 0 manually
} else {
refined_index = 0;
}
} else {
refined_index = index;
}
return vec[refined_index];
}
Then use it like:
int result = get_element<int>(myvec, 256);
Note that any index smaller than 0 starts rotating from the last element of your vector, which is of course intended.