Efficient Combinations of String Vectors - c++

I need to run an efficient function to generate string combinations. I used multiple answers in SO to write something that works. The vectors are combined, the resultant strings then sorted by character and then duplicates are removed by inserting the resultant vector into an unordered set. The function needs to run 1e7 times on long vectors (100K) and I need help running more efficient code. Here's what I have now:
vector<string> vec_combos(vector<string> v1, vector<string> v2) {
vector<string> res;
unordered_set<string> s;
for(int i=0;i<v1.size();i++) {
for(int j=0;j<v2.size();j++){
res.push_back(v1[i]+v2[j]);
sort(res.back().begin(),res.back().end());
}
}
for( int i = 0; i < res.size(); ++i ) s.insert( res[i] );
res.assign( s.begin(), s.end() );
return res;
}
int main() {
vector<string> v1 = {"ab","bc","ca"}, v2 = {"a","b"},v3;
// combined vector = {"aba","bca","caa","abb","bcb","cab"}
// sort string chars = {"aab","abc","aac","abb","bbc","abc"}
// remove duplicates = {"aab","abc","aac","abb","bbc"}
v3 = vec_combos(v1, v2);
for(int i=0;i<v3.size();i++) {
cout << v3[i] << ",";
}
return(0);
}

Passing by reference, and avoid temporary unneeded container.
You may use something like:
std::vector<std::string> vec_combos2(const std::vector<std::string>& v1,
const std::vector<std::string>& v2)
{
std::unordered_set<std::string> words; // Or std::set<std::string>
for (const auto& s1 : v1) {
for (const auto& s2 : v2) {
std::string s = s1 + s2;
std::sort(s.begin(), s.end());
words.insert(std::move(s));
}
}
return { words.begin(), words.end() };
}
Demo

Related

Converting a Vector of Characters to a Vector of Strings in C++

for the following problem, I need to return a vector of strings, where each element in the vector is of length 1. I have an answer that generates a vector of characters. I think it does what I need, but I'm not sure how to return the result as a vector of strings of length one. See my implementation below. Note, I can't change the return type of the function, it must be a vector of strings. Given that I operate on characters in my solution, I'm not sure how I'd change it.
vector<string> commonChars(vector<string>& A) {
vector<char> resVec;
unordered_map<char, int> mapObj;
for (const auto& str : A) {
for (const auto& letter : str) {
mapObj[letter]++;
}
}
int sizeOfInput = A.size();
for (int i{}; i < A[0].size(); i++) {
if (!mapObj.count(A[0][i])) continue;
resVec.insert(resVec.begin(), mapObj[(A[i])] / sizeOfInput, A[i]);
}
return resVec;
}
std::vector<char> resVec;
...
std::vector<std::string> returnVec;
returnVec.reserve(resVec.size());
for (char ch : resVec) {
returnVec.push_back(std::string(1, ch));
// or: returnVec.emplace_back(1, ch);
}
return returnVec;
Alternatively:
std::vector<char> resVec;
...
std::vector<std::string> returnVec;
returnVec.reserve(resVec.size());
std::transform(resVec.begin(), resVec.end(), std::back_inserter(returnVec),
[](char ch){ return std::string(1, ch); }
);
return returnVec;
Alternatively:
std::vector<char> resVec;
...
std::vector<std::string> returnVec(resVec.size());
std::transform(resVec.begin(), resVec.end(), returnVec.begin(),
[](char ch){ return std::string(1, ch); }
);
return returnVec;
That said, you could just eliminate the std::vector<char> altogether and just populate the final std::vector<std::string> directly from the std::map data:
std::vector<std::string> commonChars(std::vector<std::string>& A) {
std::vector<std::string> resVec;
std::unordered_map<char, int> mapObj;
for (const auto& str : A) {
for (const auto& letter : str) {
mapObj[letter]++;
}
}
size_t sizeOfInput = A.size();
if (sizeOfInput > 0) {
for (const auto& letter : A[0]) {
auto iter = mapObj.find(letter);
if (iter != mapObj.end()) {
resVec.insert(resVec.begin(), iter->second / sizeOfInput, std::string(1, letter));
}
}
}
return resVec;
}
vector<string> ret;
for(char ch: resVec)
ret.push_back(string(1,ch));
return ret;
This is a simple way of converting a std::vector<char> to a std::vector<std::string>.
If you are interested, you can also take a look at: Convert a single character to a string?
In short string(1,ch) is what does the actual conversion.
There's a different way to approach the problem. You could count the occurrences of each letter a-z in a vector for each string and loop through each vector, adding the minimum number of times a character occurs in all strings:
vector<string> commonChars(vector<string>& A) {
const int maxTimes=100;
vector<string> resVec;
vector<vector<int>> counts;
counts.reserve(A.size());
//Count occurrences of letters in each string
for (const auto& word : A) {
vector<int> count(26, 0);
for (const auto letter : word) {
count[letter-'a']++;
}
counts.push_back(count);
}
//Add minimum number of times a letter occurs in all strings
for (int i=0; i<26; ++i)
{
int min=maxTimes;
for (auto &count : counts)
{
if (count[i] < min) {
min = count[i];
}
}
for (int j=0; j<min; ++j) {
resVec.push_back(string(1, i+'a'));
}
}
return resVec;
}
Note: string(1, i+'a') creates a string of size 1 from a single char (value: i+'a').

Split vector to multiple array/vector C++

I have problem to split a string vector to smaller integer vector\array. My input vector data looks like:
std::vector<std::string> v(2);
v[0] = "0 14 150";
v[1] = "1 2 220";
//...
I know one solution, to make three arrays and to use sstream to convert data to integer. But i want to avoid making "spaghetti" code.
Thank you,
Peter.
I found a split function at stackoverflow some time ago. Unfortunatly, I cannot post the link anymore.
void split(const std::string & str, std::vector<std::string>& cont, const std::string & delims)
{
std::size_t current, previous = 0;
current = str.find_first_of(delims);
while (current != std::string::npos)
{
cont.push_back(std::move(str.substr(previous, current - previous)));
previous = current + 1;
current = str.find_first_of(delims, previous);
}
cont.push_back(std::move(str.substr(previous, current - previous)));
}
I will need delimiter in your strings (seems to be backspace in your case) and call the function on each element of your string vector:
int main()
{
std::vector<std::string> vec{ "0 14 150","1 2 220" };
std::vector<std::vector<int>> intVec(3,std::vector<int>(vec.size()));
for (int i = 0; i < vec.size(); i++)
{
std::vector<std::string> singleStr;
split(vec[i], singleStr, " ");
for (int j=0; j < singleStr.size();j++)
intVec[j][i] = (std::stoi(singleStr[j]));
}
system("pause");
}
A more generic solution could look like this. You can add further types to BasicVariant
#include <string>
#include <vector>
class BasicVariant
{
private:
std::string str;
public:
BasicVariant(const std::string& _str) :str(_str) {}
BasicVariant(int value) :str(std::to_string(value)) {}
BasicVariant(double value) :str(std::to_string(value)) {}
inline int toInt()const { return *this; }
inline double toDouble()const { return *this; }
inline std::string toString()const { return *this; }
inline bool toBool()const { return toDouble(); }
inline operator int()const { return std::stoi(str); }
inline operator double()const { return std::stof(str); }
inline operator std::string()const { return str; }
inline operator bool()const { return toDouble(); }
};
template<typename T>
void split(const std::string& str, std::vector<T>& sink, const std::string& delims)
{
std::size_t current, previous = 0;
current = str.find_first_of(delims);
while (current != std::string::npos)
{
sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
previous = current + 1;
current = str.find_first_of(delims, previous);
}
sink.push_back(std::move(BasicVariant(str.substr(previous, current - previous))));
}
int main()
{
std::vector<std::string> vec{ "0 14 150","1 2 220" };
std::vector<std::vector<int>> intVec(3, std::vector<int>(vec.size()));
for (int i = 0; i < vec.size(); i++)
{
std::vector<int> row;
split(vec[i], row, " ");
for (int j = 0; j < row.size(); j++)
intVec[j][i] = row[j];
}
system("pause");
}
Edit: I removed a verbose transposing function.
I assume that you want to convert std::vector<std::string> to a 2D matrix std::vector<std::vector<int>>.
For instance, for your example, the desired result is assumed to be arr1 = {0,1,...}, arr2 = {14,2,...} and arr3 = {150,220,...}.
First,
We can use std::istream_iterator to extract integers from strings.
We can also apply the range constructor to create a std::vector<int> corresponding to each string.
So the following function would work for you and it does not seem to be a spaghetti code at least to me.
First, this function extract two integer arrays {0,14,150,...} and {1,2,220,...} as matrices from a passed string vector v.
Since a default constructed std::istream_iterator is an end-of-stream iterator, each range constructor reads each string until it fails to read the next value.
And finally, transposed one is returned:
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
template <typename T>
auto extractNumbers(const std::vector<std::string>& v)
{
std::vector<std::vector<T>> extracted;
extracted.reserve(v.size());
for(auto& s : v)
{
std::stringstream ss(s);
std::istream_iterator<T> begin(ss), end; //defaulted end-of-stream iterator.
extracted.emplace_back(begin, end);
}
// this also validates following access to extracted[0].
if(extracted.empty()){
return extracted;
}
decltype(extracted) transposed(extracted[0].size());
for(std::size_t i=0; i<transposed.size(); ++i){
for(std::size_t j=0; j<extracted.size(); ++j){
transposed.at(i).push_back(std::move(extracted.at(j).at(i)));
}
}
return transposed;
}
Then you can extract integers from a string vector as follows:
DEMO
std::vector<std::string> v(n);
v[0] = "0 14 150";
v[1] = "1 2 220";
...
v[n-1] = "...";
auto matrix = extractNumbers<int>(v);
where matrix[0] is arr1, matrix[1] is arr2, and so on.
We can also quickly get internal pointers of them by auto arr1 = std::move(matrix[0]);.
We have here some misunderstands.
Output of my program should have three arrays/vectors.
The output looks like:
arr1| arr1| arr3
0 | 14 | 150
1 | 2 | 220
2 | 4 | 130

recursive permutations using vector

I have this function that is suppose to return all possible permutation of integers inside the vector. The code is based from an existing code that does a permutation of strings, I tried to remodeled it to work on vectors but apparently, they dont work similarly as I thought..
I'll appreciate any help that you could offer thanks;
vector<vector<int>> permute(vector<int> &v1, vector<int> &v2){
vector<vector<int>> v;
if( v1.empty() )
{
v.push_back(v2);
return v;
}
for(auto it = v1.begin(); it != v1.end(); it++){
vector<int> temp1 = v1;
temp1.erase(it); //there's a runtime error on this line
vector<int> temp2 = v2;
temp2.push_back(*it);
permute(temp1, temp2);
}
return v;
}
This is the original code that permutes a string.
void string_permutation( std::string& orig, std::string& perm )
{
if( orig.empty() )
{
std::cout<<perm<<std::endl;
return;
}
for(int i=0;i<orig.size();++i)
{
std::string orig2 = orig;
orig2.erase(i,1);
std::string perm2 = perm;
perm2 += orig.at(i);
string_permutation(orig2,perm2);
}
}
Here you go:
template < typename T>
void vec_permute( std::vector<T> &orig, std::vector<T> &perm)
{
if(orig.empty())
{
for( auto &x : perm)
std::cout<<x;
std::cout<<"\n";
return;
}
for(typename std::vector<T>::size_type i=0;i <orig.size();++i)
{
std::vector<T> orig2(orig);
orig2.erase(std::find(orig2.begin(),orig2.end(),orig.at(i)));
std::vector<T> perm2(perm);
perm2.push_back(orig.at(i));
vec_permute(orig2,perm2);
}
}
Demo: http://coliru.stacked-crooked.com/a/01ded4b778aa4165
Iterators can only be used with the container that you instanciated them with

How to write Comp function in sort() of C++ STL

In the code below, strArray is a array of strings, and vec contains corresponding ID of each string.
I want to sort IDs in vec based on strings in strArray.
How can I design the Comp function in sort?
class myClass
{
public:
int V;
vector<int> vec;
string* strArray;
myClass(int v);
void myFunc();
}
myClass::myClass(int v, vector<int> vec1)
{
V = v;
strArray = new string[v];
vec.swap(vec1)
}
void myClass::myFunc()
{
//....
for(i = 0; i<V; i++)
strArray[i] = GenerateString(vec[i]);// GenerateString() can return a string.
sort(vec.begin(), vec.end(), Comp);// sort vec based on strArray,how to design the Comp funtion?
//....
}
main()
{
int myints[] = {32,71,12,45,26,80,53,33};
vector<int> myvector (myints, myints+8);
myClass obj(8, myvector);
obj.myFunc();
return 0;
}
Change member string* strArray; to std::pair<std::string, int> strArray;, change allocation strArray = new std::pair<std::string, int>[v]; and make the following changes in your myFunc
void myClass::myFunc()
{
//....
for(i = 0; i<V; i++)
strArray[i] = make_pair(GenerateString(vec[i]), vec[i]);// GenerateString() can return a string.
sort(vec.begin(), vec.end());
std::transform(
strArray,
strArray + V,
vec.begin(),
[](const std::pair<std::string, int> &p) -> int { return p.second; }
);
//....
}
In this code we couple or pair the number (vec[i]) that generated the string and sort the array. Now using std::transform, the number is copied back to the vec
From comment
If you don't understand lambda function, you can also write a function elsewhere and pass it as fourth argument in std::transform
static int fetch_int_from_pair(const std::pair<std::string, int> &p) {
return p.second;
}
...
td::transform(
strArray,
strArray + V,
vec.begin(),
fetch_int_from_pair
);

Convert a set to a vector of vector

I'm new to C++ and I'm trying to convert unordered_set<string> to vector<vector<int>>
The set contains ("1,2,2","1","1,2","2","2,2"), and each element is a string.
I would like to output a vector<vector<int>> containing
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
So how would I parse each element (string) and make it looks like the above scheme?
#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
( std::ostringstream() << std::dec << x ) ).str()
vector<vector<int> > subsetsWithDup(const vector<int> &num) {
unordered_set<string> result;
for (int i = 0; i < num.size(); i++)
{
result.insert(SSTR(num[i]));
for (int j = i+1; j < num.size(); j++)
{
string d = SSTR(num[i]) + "," +SSTR(num[j]);
result.insert(d);
}
}
string lastString= "";
for (int i = 0; i < num.size(); i++)
{
if ( i == num.size() -1)
{
lastString+= SSTR(num[i]);
}
else
{
lastString+= SSTR(num[i])+",";
}
}
result.insert(lastString);
// convert result back to vector<vector<int>>
return result;
}
If you don't need to validate your strings you can do a transform to construct the vector<vector<int>>:
set<string> foo{ "1,2,2", "1", "1,2", "2", "2,2" };
vector<vector<int>> bar(foo.size());
transform(foo.begin(), foo.end(), bar.begin(), [](const string& i){
vector<int> result;
auto it = const_cast<char*>(i.c_str());
for (result.push_back(static_cast<int>(strtol(it, &it, 10)));
it < i.c_str() + i.size();
result.push_back(static_cast<int>(strtol(++it, &it, 10))));
return result;
});
The lambda in the transform will step through the string:
Start it at the beginning of the string
Using strtol to extract each number
Pushing each number into result
Stepping over each ',' with ++it
Return the constructed result
Prerequisites: This lambda assumes that your input set doesn't contain:
An empty string
A string that begins or ends with a comma (as in "1,2,")
A string that has consecutive commas (as in "1,,2")
That the string contains only contains digits and commas
Note: As a general rule a const_cast is bad so I wanted to comment on why I'm making one. Note that *it is never written to, only it is written to. So this is not violating the const-ness of const string& i. An alternative to strtol that doesn't require a const_cast is stoi, but until we get string_view from C++14 we'll need to construct a new string at each comma, so that's dreadfully inefficient.
One of the possible solution can be:
vector<string> split (string str, string seq) {
vector<string> ret {};
size_t pos {};
while ((pos = str.find (seq)) != string::npos) {
ret.push_back (str.substr (0, pos));
str = str.substr (pos+seq.size ());
}
ret.push_back (str);
return ret;
}
vector<int> to_vec_int (vector<string>&& vec) {
vector<int> ret {};
for (const auto& v : vec) {
ret.push_back (stoi (v));
}
return ret;
}
int main () {
unordered_set<string> st {"1,2,2","1","1,2","2","2,2"};
vector<vector<int>> vec {};
for (const auto& s : st) {
vec.push_back (to_vec_int (split (s, ",")));
}
for (const auto& v : vec) {
for (const auto& s : v) {
cout << s << " ";
}
cout << endl;
}
return 0;
}
Since your elements are delimited by comma, we can split them using split () function and we get vector of strings. This vector has to be converted to vector of int, which is the reason of existence of to_vec_int ().
Here is another possible solution using istringstream to find the commas:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <sstream>
void main()
{
using namespace std;
unordered_set<string> strSet;
strSet.insert("1,2,2");
strSet.insert("1");
strSet.insert("1,2");
strSet.insert("2");
strSet.insert("2,2");
vector<int> nums;
vector<vector<int>> arr_of_nums;
for (const auto &str : strSet) {
istringstream strStream(str);
string strToInt;
while (getline(strStream, strToInt, ',')) {
nums.push_back(stoi(strToInt));
}
arr_of_nums.push_back(nums);
nums.clear();
}
for(const auto &nums : arr_of_nums) {
for (const auto &num : nums) {
cout << num << ",";
}
cout << endl;
}
}