-1

I'm trying to write a program that starts by reading integer n from user input. n represents the total number of strings that would be processed by the program. The program then reads n character strings from standard input and stores it in an array called input.

How do I proceed with comparing/sorting input numerically? For example "aaa" is 3*97 and "bbb" is 3*98. if input = ["aaa", "bbb"] then output will ["bbb", "aaa"] after comparing and sorting.

I also have to print the output. I'm new to C++ and so far my code is:

#include <iostream>
#include <string.h>
using namespace std;

string compare (string *input, int *mains)
{
  string temp;
  for (int j=0 ; j<*mains ; j++){
    if (strcmp(&input[j][0], &input[j+1][0]) == 0)
      continue;
    else {
      swap(input[j], input[j+1]);
    }
  }
  return (*input);
}

int main()
{
  int n,i;
  cin >> n;
  string mains[n];
  for (i=0 ; i<n ; i++){
    cin >> mains[i];
  }
  cout << compare(mains, &n);
  return 0;
}

Sample input:

5
aaaaaaaaaa
ababababab
bbbbbbbbbb
acacacacad
ddaaaaaaaa

Output:

aaaaaaaaaa
ababababab
ddaaaaaaaa
bbbbbbbbbb
acacacacad

I will appreciate your help; thank you.

smac89
  • 39,374
  • 15
  • 132
  • 179
Semiron
  • 47
  • 7
  • In your `compare` function dont dereference `input` ..instead use `if (strcmp(&input[j], &input[j+1]) == 0)` and `std::swap(input[j], input[j+1])` –  Dec 20 '19 at 16:19
  • Also change name of string array in `main` function --> `std::string mains[n];` –  Dec 20 '19 at 16:21
  • 1
    Also, `string mains[n]` is **not** valid C++. VLAs are an extension. – ChrisMM Dec 20 '19 at 16:25
  • @josh7115 Thanks sir, I've done the things you've said but I still can't compile the code. I've edited the main post and added my new code. check it please – Semiron Dec 20 '19 at 16:25
  • You can lose the `strcmp`: `if (input[j] == input[j+1])` – user4581301 Dec 20 '19 at 16:25
  • `compare(&mains[], &n);` -> compare(mains, &n);` [Arrays decay to pointers](https://stackoverflow.com/questions/1461432/what-is-array-decaying). – user4581301 Dec 20 '19 at 16:27
  • `mains` is never changed inside `compare`. No need to pas it by reference. – user4581301 Dec 20 '19 at 16:28
  • Sorry, I meant `if (strcmp(&input[j][0], &input[j+1][0]) == 0)` or you can just do `if (input[j] == input[j+1])`. –  Dec 20 '19 at 16:28
  • @user4581301 I've changed the code in the main post, look at it please. but now it won't print anything – Semiron Dec 20 '19 at 16:35
  • @josh7115 please check the new code in the main post; I've done all changes but now it doesn't have any output. what should I do now? btw thanks for your helps – Semiron Dec 20 '19 at 16:35
  • Unrelated: `return (*input);` returns one (and only one) `string`. `cout << compare(mains, &n);` will not print quite what you want. You will need a loop and don't have to return anything since the contents of `input` were modified in place. OK. Post edit it is related. – user4581301 Dec 20 '19 at 16:36
  • I'm trying to compile this ..hold on –  Dec 20 '19 at 16:38
  • One of the things that makes a sorting algorithm like this brutally slow is it has to make repeated passes through the array. One loop will not be enough (unless the loop's iteration logic is overly complicated and does the same amount of work) – user4581301 Dec 20 '19 at 16:41
  • @user4581301 our teacher said we have to return array from function output.. the problem is this. tbh I'm totally confused. I've wrote the code from 0 about 15 times and don't know what to do about it now. – Semiron Dec 20 '19 at 16:42
  • Save yourself some trouble and remove the user input for now. Hard code the input in `mains`. Lets you test and debug faster. – user4581301 Dec 20 '19 at 16:42
  • change return type of `compare` function to `void`, delete the `return` statement in that function and then in `main` function run a loop to print out value at each index. –  Dec 20 '19 at 16:45
  • @josh7115 the problem is that our teacher said: You will not receive a score if it is not returned via the array function output. – Semiron Dec 20 '19 at 16:46
  • @user4581301 What's your idea now? what to do? – Semiron Dec 20 '19 at 16:51
  • 1
    Make a plan. Never write a line of code without a plan. If you don't know exactly what that line is supposed to do and how it works in context with the lines around it, stop. If you do know what it does and it doesn't make logical sense, stop and redesign. If you don't know what you are doing before you start writing code, you are doomed to rewriting the code until you do know. This wastes a lot of time. – user4581301 Dec 20 '19 at 16:54
  • You could overload `operator<<` for `string*` ..would that work for you? –  Dec 20 '19 at 16:54
  • @josh7115 I didn't understand what you said. the only rule we have is this: We have to print the array using function output. can you make it a bit more clear for me? or answer the post with a working code? that will mean a lot to me. thanks man – Semiron Dec 20 '19 at 16:58
  • You will get further with this question if you indicate also what the _expected_ output is supposed to be. – smac89 Dec 21 '19 at 05:03
  • You need to write a custom [Compare](https://en.cppreference.com/w/cpp/named_req/Compare) function to pass to [std::sort](https://en.cppreference.com/w/cpp/algorithm/sort) which would sum the ASCII values of each character in each of the two strings it takes as input and then returns `true` if string `a` sorts before string `b`, or `false` otherwise. – David C. Rankin Dec 21 '19 at 06:28

1 Answers1

0

std::sort provides a simple way of sorting container values. Many times the provided std::greater or std::less comparisons can be used without having to write a custom comparison function. However, in your case, you want to compare string values based on the sum the the ASCII values of each character in the string. When a custom compare function is needed, you simply write a comparison function to pass to std::sort. The prototype is:

bool cmp(const Type1 &a, const Type2 &b);

Your function simply needs to iterate over each value in each string saving the sum of the ASCII values and then you return true if a sorts before b, or false otherwise to tell std::sort whether to swap a and b.

std::accumulate allows you to quickly sum the values within a container (int, double, char, etc..) In the case of your std::string it can be treated as a std::vector<char> and the ASCII value of each character summed. Say into suma and sumb. Then you can simply return the comparison of suma < sumb.

Putting that together, you can write your custom comparison function as:

bool compare (const std::string& a, const std::string& b)
{
    int suma = std::accumulate(a.begin(), a.end(), 0),  /* sum values in a */
        sumb = std::accumulate(b.begin(), b.end(), 0);  /* sum values in b */

    return suma < sumb;     /* return comparison */
}

(note: to guarantee that suma and sumb do not suffer integer overflow, you must limit the number of characters in your string to 17,043,521 (or less) to cover the worst case scenario of a string with all characters '~', e.g. ASCII 126 -- that should not be a problem with any of your normal input. If it is, change suma and sumb from int to a larger size like uint64_t, etc..)

Then it is just a matter of reading the first value in input, and looping that many times reading strings input into a std::vector<std::string>. You then call std::sort on your vector of strings providing your custom compare function as the 3rd argument and then output the sorted results, e.g.

int main (void) {

    int n = 0;                          /* int for 1st value input */
    std::vector<std::string> vs {};     /* storage for each string */

    if (!(std::cin >> n) || n < 0) {    /* read n from input */
        std::cerr << "invalid format - n.\n";
        return 1;
    }   /* clear to end of line (removing \n) */
    std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');

    while (n--) {                       /* loop n times */
        std::string tmp;                /* temporary string */
        if (!getline (std::cin, tmp)) { /* read/validate input into tmp */
            std::cerr << "error: failed read of string.\n";
            return 1;
        }
        vs.push_back(tmp);              /* add to vector */
    }
    std::sort (vs.begin(), vs.end(), compare);  /* sort w/compare function */

    for (auto& s : vs)                  /* loop over sorted strings */
        std::cout << s << '\n';         /* output each string */
}

Adding the required headers and putting it altogether you can do:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <limits>

bool compare (const std::string& a, const std::string& b)
{
    int suma = std::accumulate(a.begin(), a.end(), 0),  /* sum values in a */
        sumb = std::accumulate(b.begin(), b.end(), 0);  /* sum values in b */

    return suma < sumb;     /* return comparison */
}

int main (void) {

    int n = 0;                          /* int for 1st value input */
    std::vector<std::string> vs {};     /* storage for each string */

    if (!(std::cin >> n) || n < 0) {    /* read n from input */
        std::cerr << "invalid format - n.\n";
        return 1;
    }   /* clear to end of line (removing \n) */
    std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');

    while (n--) {                       /* loop n times */
        std::string tmp;                /* temporary string */
        if (!getline (std::cin, tmp)) { /* read/validate input into tmp */
            std::cerr << "error: failed read of string.\n";
            return 1;
        }
        vs.push_back(tmp);              /* add to vector */
    }
    std::sort (vs.begin(), vs.end(), compare);  /* sort w/compare function */

    for (auto& s : vs)                  /* loop over sorted strings */
        std::cout << s << '\n';         /* output each string */
}

Example Input

$ cat dat/asciisum.txt
5
aaaaaaaaaa
ababababab
bbbbbbbbbb
acacacacad
ddaaaaaaaa

Example Use/Output

$ ./bin/sortasciivalsum < dat/asciisum.txt
aaaaaaaaaa
ababababab
ddaaaaaaaa
bbbbbbbbbb
acacacacad

Selection Sort Instead of std::sort

To use a selection-sort instead of the std::sort algorithm, you would need to replace the compare() function with:

void selsort (std::vector<std::string>& vs)
{
    for (size_t i = 0; i < vs.size() - 1; i++) {
        size_t minidx = i;                              /* set minimum index */
        for (size_t j = i + 1; j < vs.size(); j++) {    /* find new minidx */
            /* compare sum of ASCII values between vs[j] & vs[minidx] */
            if (std::accumulate(vs[j].begin(), vs[j].end(), 0) <
                std::accumulate(vs[minidx].begin(), vs[minidx].end(), 0))
                minidx = j;                 /* set min index to lesser */
        }
        if (minidx != i)                    /* if min index changed */
            std::swap (vs[i], vs[minidx]);  /* swap strings */
    }
}

and then replace:

    std::sort (vs.begin(), vs.end(), compare);  /* sort w/compare function */

with

    selsort (vs);                       /* selection sort based on ASCII sum */

The selection sort takes a reference to your collection of strings (your std::vector<std::string>) and then compares the sum of the strings using std::accumulate in the same way the compare() function did in order to sort the strings according to the numeric sum of the characters contained in the string. The selection sort sequentially finds the string with the lowest sum and swaps those to the beginning in order.

As in the comment, you are always better off using std:sort rather than a sort you write. std::sort has been tested a billion times over and will be much more efficient. (however a selection sort isn't bad...)

Make the changes, look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Huh? What compiler? Compiles fine without warning on g++, e.g. `g++ -Wall -Wextra -pedantic -Wshadow -std=gnu++11 -Ofast -o bin/sortasciivalsum sortasciivalsum.cpp` You must use `-std=c++11` minimum. – David C. Rankin Dec 22 '19 at 06:57
  • [Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11 [Error] in C++98 'vs' must be initialized by constructor, not by '{...}' [Error] range-based 'for' loops are not allowed in C++98 mode – Semiron Dec 22 '19 at 06:57
  • and I compile with GCC – Semiron Dec 22 '19 at 06:58
  • Are you limited to `C++98`? Can you add the `-std=c++11` compiler option? Do this `g++ -Wall -Wextra -pedantic -Wshadow -std=c++11 -Ofast -o nameyouwant yoursource.cpp` (or use `-O3` instead of `-Ofast` if using gcc < 4.6) – David C. Rankin Dec 22 '19 at 06:59
  • I got confused :( can you explain a bit more clear? sorry for that – Semiron Dec 22 '19 at 07:04
  • Sure, the standard templates like `std::vector` and the *range-based* `for` loops need C++11. To compile with C++11, add the `-std=c++11` compiler option to your compile string. Minimum you can do, `g++ -std=c++11 -o nameyouwant yoursource.cpp`. That will tell gcc (g++) to compile using the C++11 standard. If you are using an IDE like VS or Code::Blocks, just click on your project property dialog and you can set compiler options you need. I always recommend `-Wall -Wextra -pedantic` (and `-Wshadow`), and don't accept code until it compiles without a single warning. – David C. Rankin Dec 22 '19 at 07:06
  • If you are using an IDE instead of compiling from the command line, let me know and I'll look up where to set the compiler option for the language standard. – David C. Rankin Dec 22 '19 at 07:10
  • I was using Dev IDE, now I've tested with code blocks and everything is fine. another question: How are you sorting the strings? – Semiron Dec 22 '19 at 07:18
  • You see where I do `std::accumulate(a.begin(), a.end(), 0),` That is just summing the character values in string a so the total of the characters (e.g. `'a'` + `'b'` + `'a'`..., [`97 + 98 +97...`]) can be compared with the sum for string b in the *compare()* function. Under the `for (auto& s : vs)` loop, you can replace with `std::cout << s << " - " << std::accumulate(s.begin(), s.end(), 0) << '\n';` to see the total value for each string and how they are sorted. – David C. Rankin Dec 22 '19 at 07:21
  • Can we use Selection Sort instead of using the sort function? It's one of the problem rules. I want to learn if I can do it with selection sort or not – Semiron Dec 22 '19 at 07:21
  • Sure, rolling-your-own sort is just not nearly as reliable as using `std::sort`, but you can use a selection sort on the string totals instead of letting `std::sort` do it. I'll have to look up a quick selection sort algorithm, but it shouldn't be difficult. – David C. Rankin Dec 22 '19 at 07:24
  • could you edit the post and use selection sort instead of sort function? I'm new to CPP and I'm just learning things now :) anyway, your work was fantastic. it meant a lot to me. everyone is just ignoring me when I ask questions, but you helped me so much. thanks – Semiron Dec 22 '19 at 07:27
  • Yes, give me a minute, I've got to write it in some sane way `:)` – David C. Rankin Dec 22 '19 at 07:33
  • amazing job; thanks man. another question. how can we print the array with function output? is it possible? if yes how? – Semiron Dec 22 '19 at 08:05
  • Sure, you can just pass a reference to `vs` (your vector of strings) to a function and then just move the range-based for loop that is currently printing things into the function, e.g. `void prnvector (std::vector& vs) { for (auto& s : vs) std::cout << s << '\n'; }` done. – David C. Rankin Dec 22 '19 at 08:07
  • Good luck with your coding and [cppreference.com](https://en.cppreference.com/) every time you start scratching your head. Cryptic at first, but it is one of the (if not the) best C++ reference on the net. – David C. Rankin Dec 22 '19 at 08:13
  • Hello, bro I faced into a problem and no one is helping? can I ask you some questions? :) I couldn't send you private message. do you have any social media I can message you? it's a small bug – Semiron Jan 10 '20 at 07:09
  • Sure, if you don't want to ask a new question, then send to drankinatty at good ole gmail. I'll take a look. – David C. Rankin Jan 10 '20 at 07:15
  • https://stackoverflow.com/questions/59666725/creating-minesweeper-console-game-using-c -- check this please :) you are the best man – Semiron Jan 10 '20 at 07:19
  • Hello again, can I have your email address maybe? :) chatting is not allowed here.. – Semiron Jan 10 '20 at 20:25
  • I gave it a comment ago, drankinatty at gmail will work. – David C. Rankin Jan 10 '20 at 20:34