Remove duplicates from the string in CPP - c++

I wrote the following code for removing the duplicates from a given string i.e. if ARRUN is the input then the output will be ARUN.
#include <bits/stdc++.h>
using namespace std;
char* removeDuplicates(string &s,int n){
char arr[n];
unordered_map<char,int> exists;
int index = 0;
for(int i=0;i<n;i++){
if(exists[s[i]]==0)
{
arr[index++] = s[i];
exists[s[i]]++;
}
}
return arr;
}
//driver code
int main(){
string str;
cin >> str;
cout<<removeDuplicates(str,str.length())<<endl;
return 0;
}
The code produces no output at all, however, it works fine if I use char arr[] instead of string class.

You can't use char arr[n] without being n constant or constexpr.
You don't need map. set is sufficient.
Note that map and set remove duplicates already, then you can check if any element is inserted or not to get your new string in the same order of the first, as follows
#include<string>
#include<iostream>
#include<unordered_set>
std::string removeDuplicates(const std::string &s){
std::string arr;
std::unordered_set<char> exists;
for(const auto&el:s)
if(exists.insert(el).second) arr+=el;
return arr;
}
//driver code
int main(){
std::string str;
std::cin >> str;
std::cout<<removeDuplicates(str)<<std::endl;
return 0;
}

std::string support removing elements.
#include <iostream>
#include <string>
std::string removeDuplicates(std::string str) {
for (int i = 0; i < str.size(); i++) {
while (true) {
int j = str.find_last_of(str[i]);
if (i < j) {
str.erase(j, 1);
} else {
break;
}
}
}
return str;
}
int main() {
std::cout << removeDuplicates("ARRUN");
return 0;
}

If a function declaration looks the following way
char* removeDuplicates(string &s,int n);
then it means that the passed object itself will be changed in the function. Otherwise the parameter shall have the qualifier const.
Also it is unclear why the function has return type char *. It looks like the declaration of the function is contradictive.
The second parameter of the function shall have at least the type size_t or that is better std::string::size_type. The type int can not accomodate all values of the type std::string::size_type.
The function could be declared without the second parameter.
A straightforward approach without using intermediate containers that requires dynamic memory allocation can look the following way
#include <iostream>
#include <string>
std::string & removeDuplicate( std::string &s )
{
const char *p = s.c_str();
std::string::size_type pos = 0;
for ( std::string::size_type i = 0, n = s.size(); i < n; i++ )
{
std::string::size_type j = 0;
while ( j < pos && s[i] != s[j] ) j++;
if ( j == pos )
{
if ( i != pos ) s[pos] = s[i];
++pos;
}
}
return s.erase( pos );
}
int main()
{
std::string s( "H e l l o" );
std::cout << "\"" << s <<"\"\n";
std::cout << "\"" << removeDuplicate( s ) <<"\"\n";
return 0;
}
The program output is
"H e l l o"
"H elo"

#Arun Suryan, You pointed out correctly. But you can do it without using vector by using global char array.
Also don't forget to append the newline at the end!
Have a look at the following code:
#include<string>
#include<iostream>
#include<unordered_map>
char* removeDuplicates(std::string &s,int n){
std::unordered_map<char,int> exists;
char* arr = (char*)(malloc(n*sizeof(char)));
int index = 0;
for(int i=0;i<n;i++){
if(exists[s[i]]==0)
{
arr[index++] = s[i];
exists[s[i]]++;
}
}
arr[index] = '\n';
return arr;
}
//driver code
int main(){
std::string str;
std::cin >> str;
std::cout<<removeDuplicates(str,str.length())<<std::endl;
return 0;
}

This might be a bit advanced for newcomers to C++ but another solution makes use of the erase-remove idiom:
std::string removeDuplicates(const std::string& s) {
std::string result = s;
std::unordered_set<char> seen;
result.erase(std::remove_if(result.begin(), result.end(), [&seen](char c)
{
if (seen.find(c) != seen.end())
return true;
seen.insert(c);
return false;
}),
result.end());
return result;
}
It basically uses a set to store characters that have been seen, shuffles the characters to be removed down to the tail (using std::remove_if) and erases the tail from the string.
Working version here.

This works too, a single line solution with an inbuild function.
cout<<str.erase(std::unique(str.begin(), str.end()), str.end());

Simple Answer
#include<bits/stdc++.h>
using namespace std;
string removeduplicate(string key){
set<char>s;
string ans="";
for(int i=0;i<key.size();++i){
if(s.find(key[i])==s.end()){
s.insert(key[i]);
ans.push_back(key[i]);
}
}
return ans;
}
int main()
{
string key="";
cout<<"enter the key:";
cin>>key;
string ans1=removeduplicate(key);
cout<<ans1;
return 0;
}

So, after doing a bit of reading on the Internet I realized that I was trying to return a pointer to the local array in the removeDuplicates() function.
This is what works fine
#include <bits/stdc++.h>
using namespace std;
void removeDuplicates(string &s,int n){
vector<char> vec;
unordered_map<char,int> exists;
int index = 0;
for(int i=0;i<n;i++){
if(exists[s[i]]==0)
{
vec.push_back(s[i]);
exists[s[i]]++;
}
}
for(auto x: vec)
cout << x;
}
//driver code
int main(){
string str;
cin >> str;
removeDuplicates(str,str.length());
return 0;
}
PS: We can make the return type of function to vector as well.

Related

String function optimisation?

I'm new to C++ and i just wrote a function to tell me if certain characters in a string repeat or not:
bool repeats(string s)
{
int len = s.size(), c = 0;
for(int i = 0; i < len; i++){
for(int k = 0; k < len; k++){
if(i != k && s[i] == s[k]){
c++;
}
}
}
return c;
}
...but i can't help but think it's a bit congested for what it's supposed to do. Is there any way i could write such a function in less lines?
Is there any way i could write such a function in less lines?
With std, you might do:
bool repeats(const std::string& s)
{
return std::/*unordered_*/set<char>{s.begin(), s.end()}.size() != s.size();
}
#include <algorithm>
bool repeats(std::string s){
for (auto c : s){
if(std::count(s.begin(), s.end(), c) - 1)
return true;
}
return false;
}
Assuming you are not looking for repeated substrings :
#include <iostream>
#include <string>
#include <set>
std::set<char> ignore_characters{ ' ', '\n' };
bool has_repeated_characters(const std::string& input)
{
// std::set<char> is a collection of unique characters
std::set<char> seen_characters{};
// loop over all characters in the input string
for (const auto& c : input)
{
// skip characters to ignore, like spaces
if (ignore_characters.find(c) == ignore_characters.end())
{
// check if the set contains the character, in C++20 : seen_characters.contains(c)
// and maybe you need to do something with "std::tolower()" here too
if (seen_characters.find(c) != seen_characters.end())
{
return true;
}
// add the character to the set, we've now seen it
seen_characters.insert(c);
}
}
return false;
}
void show_has_repeated_characters(const std::string& input)
{
std::cout << "'" << input << "' ";
if (has_repeated_characters(input))
{
std::cout << "has repeated characters\n";
}
else
{
std::cout << "doesn't have repeated characters\n";
}
}
int main()
{
show_has_repeated_characters("Hello world");
show_has_repeated_characters("The fast boy");
return 0;
}
std::string str;
... fill your string here...
int counts[256]={0};
for(auto s:str)
counts[(unsigned char)s]++;
for(int i=0;i<256;i++)
if(counts[i]>1) return true;
return false;
6 lines instead of 9
O(n+256) instead of O(n^2)
This is your new compact function :
#include <iostream>
#include <algorithm>
using namespace std;
int occurrences(string s, char c) {
return count(s.begin(), s.end(), c); }
int main() {
//occurrences count how many times char is repetated.
//any number other than 0 is considered true.
occurrences("Hello World!",'x')?cout<<"repeats!":cout<<"no repeats!";
//It is equal write
//
// if(occurrences("Hello World!",'x'))
// cout<<"repeats!";
// else
// cout<<"no repeats!";
//So to count the occurrences
//
// int count = occurrences("Hello World!",'x');
}

Why am I getting incomplete reversed string

For the string input like this - "i.like.this.program.very.much", the output should be "much.very.program.this.like.i".
This is my code -
void func(string s,int n){
vector<string>v;
string temp="";
for(int j=0;j<n;j++){
if(s[j]!='.'){
temp+=s[j];
}
else{
v.push_back(temp);
temp="";
}
}
reverse(v.begin(),v.end());
for(int i=0;i<v.size();i++){
cout<<v[i]<<".";
}
cout<<"\n";
}
Here 'n' is the string length and 's' is the string. I'm getting output as - "very.program.this.like.i."
The else block only executes when a period is encountered. Since there is no period at the end of the string it never pushes back the final word.
void func(string s, int n) {
vector<string>v;
string temp = "";
for (int j = 0; j < n; j++) {
if (s[j] != '.') {
temp += s[j];
}
else {
v.push_back(temp);
temp = "";
}
}
// v.push_back(temp); This will push the last word / letters
reverse(v.begin(), v.end());
for (int i = 0; i < v.size(); i++) {
cout << v[i] << ".";
}
cout << "\n";
}
void func(string s,int n){
vector<string>v;
string temp="";
for(int j=0;j<n;j++){
if(s[j]!='.' || j==0){
temp+=s[j];
}
else{
v.push_back(temp);
temp="";
}
}
reverse(v.begin(),v.end());
for(int i=0;i<v.size();i++){
cout<<v[i]<<".";
}
cout<<"\n";
}
The statement in your code appends the character only when the current index character is a period.
For starters the first parameters should be a constant reference to an object of the type std::string.
void func( const string &s, int n);
But if the compiler supports the C++ 17 Standard then the first parameter is even better to make of the type std::string_view.
The second parameter is redundant.
The function should return the reversed string.
Instead of using a loop you could use a string stream.
In the loop the sub-string after the last symbol '.' is not appended to the vector.
Instead of the vector it would be better to use the container std::forward_list because you will not need to reverse its elements.
I can suggest the following function definition as it is shown in the demonstrative program below.
#include <iostream>
#include <string>
#include <forward_list>
#include <iterator>
#include <sstream>
std::string reverse( const std::string &s, char c = '.' )
{
std::istringstream is( s );
std::forward_list<std::string> list;
std::string word;
while ( getline( is, word, c ) ) list.push_front( word );
bool first = true;
std::string result;
result.reserve( s.size() );
for ( const auto &item : list )
{
if ( !first ) result += c;
else first = false;
result += item;
}
return result;
}
int main()
{
std::string s( "i.like.this.program.very.much" );
std::cout << reverse( s ) << '\n';
return 0;
}
The program output is
much.very.program.this.like.i

converting strings to integers by using "stringstream "

I am trying to convert strings of data to integers, (to use it for some calculations ) by using stringstream , but it fails when there is a space.
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
int main() {
string line;
vector <string>data;
for (int i = 0; i < 10;i++) {
getline(cin,line);
data.push_back(line);
}
///converting digits to int
vector<int> values;
int n;
char ch=',';
for (int i = 0; i < data.size();i++) {
stringstream stream(data[i]);
while( stream >>n ) {
if(stream >> ch) {
values.push_back(n);
}
else {
values.push_back(n);
}
cout<<n<<" ";
}
cout<<endl;
}
return 0;
}
input : 1,182,08 51 15 --> output : 1 182 8 1 5
there are some digits lost after spaces.
so, what can I do to avoid it?
Complete working code based on seccpur's answer. Visual Studio 2017 Community:
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
#define BUFSZ 100 // Set max size of the numbers as a single string
int convertToIntegers(char *s, vector<int> &values);
int main()
{
int count;
int i;
char data[BUFSZ];
vector<int> values;
strcpy_s(data, "1,182,08 51 15");
count = convertToIntegers(data, values);
// for (auto val : values) // Show the result
// cout << val << '\n';
// *** OR ***
for (i = 0; i < count; i++)
cout << values[i] << '\n';
}
//////////////////////////////////////
// Convert a C string to integers
//
int convertToIntegers(char *s, vector<int> &values)
{
vector<string> numbers;
char *next_token;
char* ptr = strtok_s(s, " -.,;", &next_token);
while (ptr)
{
string str(ptr);
numbers.push_back(str);
ptr = strtok_s(NULL, " -.,;", &next_token); // Next number
}
//
// Convert the resulting strings to integers
//
for (auto str : numbers)
values.push_back(stoi(str));
return (int)values.size();
}
If you know you have exactly one character as a separator, either a space, either a comma, the following code will work:
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
int main() {
string line;
vector <string>data;
for (int i = 0; i < 10;i++) {
getline(cin,line);
data.push_back(line);
}
///converting digits to int
vector<int> values;
int n;
char ch=',';
for (int i = 0; i < data.size();i++) {
stringstream stream(data[i]);
while( stream >>n ) {
char c = stream.get();
//if(stream >> ch) {
// values.push_back(n);
//}
//else {
values.push_back(n);
//}
cout<<n<<" ";
}
cout<<endl;
}
return 0;
}
You are using multiple delimiters in the input ( like whitespace : , ; -) which complicates the matter. Here's a possible snippet using std::strtok:
//Enter a line
string line;
getline(cin, line);
// Convert string to char* so that std::strtok could be used later
char *cstr = new char[line.length() + 1];
std::strcpy(cstr, line.c_str());
vector<string> words;
// split line into multiple strings using multiple delimiters
char* ptr = std::strtok(cstr, " -.,;");
while (ptr)
{
string str(ptr);
words.push_back(str);
ptr = strtok(NULL, " -.,;");
}
delete[] cstr;
// Convert string to int
vector<int> values;
for (auto str : words){
values.push_back(std::stoi(str));
}
// Print the values
for (auto val : values){
cout << val << '\n';
}

How to convert vector to string of a particular type

I have a vector in c++ of the form (1,2,3). I want this vector to be a string "1->2->3". Can anyone help me how can I convert the vector to this string?
I have tried this code but the constraint is that I can't use itoa() method or to_str.
string stringify(vector<int> v)
{
string s = "";
for(int i=0;i<v.size()-1;i++){
s = itoa(v[i]);
s = s + "->"
}
s = s+v[i];
cout<<s;
return s;
}
You could use std::stringstream:
string stringify(const vector<int>& v)
{
stringstream ss;
for (size_t i = 0; i < v.size(); i++) {
if (i != 0) ss << "->";
ss << v[i];
}
return ss.str();
}
LIVE
BTW1: You might change the parameter type from passing by value to passing by reference, to avoid the copy of the vector.
BTW2: You might make stringify a template function which could be used generically. e.g.
template <typename T>
string stringify(const vector<T>& v)
I did it in c, look if it is useful to you:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 2
void stringify(char*);
int main()
{
char v = {1,2,3};
stringify(v);
printf("\n\n");
getchar();
return 0;
}
void stringify(char v[])
{
char s[20]=" ";
char aux[20] = "";
for(int i=0;i <= SIZE;i++)
{
if(i < SIZE)
sprintf(s, "v[%d] -> ", &v[i]);
else
sprintf(s, "v[%d]", &v[i]);
printf("", strcat(aux,s));
}
printf("\n%s", aux);
}
Now the array aux contains all the output with format.
output:

C++ error: field has incomplete type 'int []'

I'm making a virtual machine in C++ and I've run into this error,
error: field has incomplete type 'int []'
int instrarr[];
I have absolutely no idea what is wrong with the int array. Can someone take a look and let me know what I've done wrong, I've been looking at it for over an hour and I can't seem to find what tiny detail I must have left out. My entire file is below incase you need it for reference.
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
class vm {
private:
string finfo;
string filedata;
string fconv;
string instruction;
int instrarr[];
int zerocount[];
public:
/* Helper functions */
int countinstrs(string s) {
int count = 0;
for (int i = 0; i < s.size(); i++)
if (s[i] == ',') count++;
return count;
}
int countlzeros(string s) {
int count = 0;
for (int i = 0; i < s.size(); i++)
if (s[i] == '0') {
count++;
} else {
i = s.size() + 1;
}
return count;
}
string load_program(string file) {
ifstream rdfile(file);
while(rdfile >> instruction) {
filedata += instruction;
filedata += ",";
}
rdfile.close();
return filedata;
}
string convert_program(string fconv) {
int instrcount = countinstrs(fconv);
stringstream hextoint;
unsigned int value;
string s = fconv;
string delimiter = ",";
size_t pos = 0;
string token;
int i = 0;
while ((pos = s.find(delimiter)) != string::npos) {
token = s.substr(0, pos);
int zeroc = countlzeros(token);
//zerocount[i] = zeroc;
stringstream hextoint(token);
hextoint >> hex >> value;
//instrarr[i] = value;
cout << value << endl;
s.erase(0, pos + delimiter.length());
i++;
}
return "";
}
void run_program(string file) {
finfo = load_program(file);
fconv = convert_program(finfo);
//execute_program();
}
};
int main(int argc, char* argv[]) {
vm rd;
rd.run_program(argv[1]);
return 0;
}
It's pretty simple, int[] is an incomplete type, as it lacks information about how large it is. In function call parameters, it goes synonymous with declaring a pointer instead of an array, but for defintions, as in your code, the compiler certainly needs to know how large the array is, in order to allocate storage for it.