0

I have a string consisting of letters and numbers. what i need to do is remove alphanumeric characters of that string so that the sequence is only 4 numbers make the biggest number and not change their position Ex

input: 24d5n4r05f704n652z393
output: 7693

I removed all the literals from the string, then i created subsequences of length 4 and converted them to integer type for comparison But I have a main problem that the substrings I have created are all contiguous substrings, I need help to create non-continuous substrings for comparison Ex:

245405704652393 (string after processing the letters)

I can only generate strings like

2, 24, 245, etc

I can't generate strings like

254 (omit number 4), 440 (omit number 5), etc

Here is my code

#include <string>
#include <vector>
#include <iostream>

using namespace std;

ifstream fi;
ofstream fo;

void tlw(string &s){  //This function converts uppercase letters to lowercase
    for(int i=0; i<s.length(); i++){
        if(s[i]>='A'&&s[i]<='Z'){
            s[i]=tolower(s[i]);
        }
    }
}
void duyet(string s, int n){  //this function let me create substrings and convert them to integer type and compare  
    int max = 0, dem;
    string new_s;
    vector<string> a;
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            new_s = s.substr(i,j-i+1);   //this is the part i need help
            if (new_s.length() == 4){
                int dem = stoi(new_s);
                if (max<dem){
                    max = dem;
                }
            }
        }
    }
    fo<<max;
}
void xuli(string s, int n){     //here is the function to handle the problem
    tlw(s);
    int e = 0;
    for(int i=0; i<n; i++){
        if (s[i] >= 'a' && s[i] <= 'z'){
            e = i; 
            s.erase(e,1);
        }
    }
    duyet(s,s.length());
}
int main(){
    fi.open("dctn.inp");fo.open("dctn.out");
    string s;
    fi>>s; int n = s.length();
    xuli(s,n);
    fi.close();fo.close();
}
Lanju
  • 11
  • 1
  • 3
    *#include * -- Use the proper, standard header files, not this one. – PaulMcKenzie Jul 31 '23 at 13:11
  • *I have a string consisting of letters and numbers. what i need to do is remove alphanumeric characters of that string* -- `s.erase(std::remove_if(s.begin(), s.end(), ::isalpha), s.end());` -- That removes all the letters in just a single line of code. Also, please give your functions meaningful names that can be understood what the function does. Names like `tlw` and `xuli` do not tell us what the purpose of the function is. – PaulMcKenzie Jul 31 '23 at 13:14
  • My teacher said using is easier to remember than having to use multiple libraries. And I'm sorry for not commenting, I'll fix the code now – Lanju Jul 31 '23 at 13:20
  • 2
    *My teacher said using is easier to remember* -- Have your teacher [read this](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h). And as far as remembering -- when you physically have to type in the headers, your brain will be trained to "remember". You use `vector`, you instantly will learn to type in `#include `. You use `std::string`, you instantly will remember to `#include `, etc. Relying on the `bits` header does not help in learning one of the most difficult languages out there. – PaulMcKenzie Jul 31 '23 at 13:21
  • About bits/c++ then your teacher is wrong (coding is NOT about coding easy quickly it is about coding correctly). And I see more signs your teacher is not doing a good job teaching C++. E.g. `using namespace std;` also not recommended, explicitily calling `fi.close()` not needed as streams are RAII classes, not teaching to pass strings by const reference). Global variables. Using naked loops instead of 'range based for loops` – Pepijn Kramer Jul 31 '23 at 13:26
  • Can you do me a favor and ask your teacher to read this : https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines and update his training material accordingly. Even if it is an algorithm or datastructures class C++ should be taught as it is intended to be used. If he wants he can contact me. – Pepijn Kramer Jul 31 '23 at 13:27
  • The funny thing is that it is harder for me to type in the `bits` header with all of those slashes and `+` signs and get it right then to simply include the proper headers. – PaulMcKenzie Jul 31 '23 at 13:31
  • @PepijnKramer I always suggest we call `close()` explicitly to catch possible filesystem errors. A destructor will silently swallow such errors, which in real life might be undesirable. – Evg Jul 31 '23 at 13:32
  • 1
    I saw everyone's comments and corrected them – Lanju Jul 31 '23 at 13:32
  • `#include ` --> The correct header is `#include `. – PaulMcKenzie Jul 31 '23 at 13:47
  • @Evg Fair point, never had any issues with not calling it (but absence of proof and all...) ;) – Pepijn Kramer Jul 31 '23 at 13:49
  • 2
    I think this is one where you should write your algorithm in pseudo-code first. 1. Strip anything that is not a digit. 2. If <=4 digits. That's the answer (all of them). Otherwise find largest first occurrence of largest digit not in last 3 - that's the lead digit. Then largest digit in the remainder not in last 2 and so on. NB: May find leading zeroes to be binned at the end if the output is a string (not an int). – Persixty Jul 31 '23 at 14:40
  • Inefficient to erase anything. Find the positions of the last, second last, third last and fourth last digits. (If there aren’t that many then use what you have and finish early.) Then use the largest digit up to the 4th last, then the largest digit up to the third last and so on. You can speed this up if you find a 9. – lastchance Jul 31 '23 at 15:20
  • @lastchance Those suggestions are valid but some may be Big-O() Neutral. That is don't change the asymptotic scaling and so may be premature optimisation. Though I would say a (full) brute force solution that generates all combinations should be avoided if easy. Though if 15 digits is typical there are still only 15*14*13*12/4/3/2=1365 candidates. Scaling is horrible but at this scale still potentially manageable. – Persixty Jul 31 '23 at 16:16
  • 1
    @Persixty, yes, you're probably right about premature optimisation. I think mine is essentially the same as yours without stripping out anything, but yours would look tidier and be easier to code. – lastchance Jul 31 '23 at 16:43
  • @PepijnKramer When you do billions of writes into some network drive, such errors happen from time to time. – Evg Jul 31 '23 at 19:46
  • @lastchance I want to be clear. I love your thinking. If we were dealing with a master sequence of 1,000,000,000 characters and looking for a subsequence of 1,000 digits then unless 9s are 'rare' then 'most' outputs are going to be 'practically' all 9s and your refinements would be valuable improvements. But I think there's more going on in this apparently trivial problem when we start to look at all scales including but not only asymptotic. – Persixty Aug 01 '23 at 07:08

2 Answers2

0

You are attempting a brute force solution for a problem that does not require one. Before taking that approach, try to analyze the problem a little more, to see if you can find a direct solution. The program below is such a solution.

It starts by eliminating all the non-digits. After that, when there are i characters still remaining to be discovered, it reserves the last i - 1 characters of the digit string, and searches the rest to find the std::max_element. It begins its search just beyond the place where the last character was found. The std::max_element found among the characters it searches is the next character in the result.

It is a good idea is to create test cases that will fail, just to make sure your algorithm is robust.

It is also important to have a running program, even when it is only partially complete. At first, function extract_largest_number simply returned the empty string! I coded the main routine, with its test_strings, and just that "stub" for function extract_largest_number. The result was obviously incorrect, but the program compiled and ran.

The next thing I coded was function eliminate_non_digits, which I knew already was going to be just a couple calls into the <algorithm> header. At this point, I added the error trapping code to function extract_largest_number, but nothing more. It simply returned the string after non-digits had been removed. Once again, the result was obviously wrong, but my program still ran. Because it ran, I was able to scan the output, and verify that function eliminate_non_digits was working correctly.

Finally, with this structure in place, I coded the actual solution, which turned out to be easier than I expected. This method of "stub-programming" often leads to that result.

Your original question states, "the sequence is only 4 numbers," so I took that as a requirement: a valid solution has exactly 4 digits. In case you need to allow shorter sequences, function extract_biggest_number has a parameter named require_exact_length. When the corresponding argument is false, digit sequences of any length are output.

One final tip: as you learn more about C++, try to use functions in the <algorithm> header as often as you can.

#include <algorithm>  // back_inserter, copy_if, max_element
#include <cctype>     // isdigit
#include <iostream>   // cout
#include <string>     // string
#include <vector>     // vector

auto eliminate_non_digits(std::string const& s) -> std::string
{
    std::string result;
    std::copy_if(s.cbegin(), s.cend(), std::back_inserter(result), 
        [](char const c) {
            return std::isdigit(c); 
        }
    );
    return result;
}

auto extract_biggest_number(
    std::string const& s,
    bool const require_exact_length = true,
    std::string::size_type const length = 4u
) -> std::string
{
    std::string result, digits{ eliminate_non_digits(s) };
    if (digits.size() <= length)
        result = digits;
    else if (length) {
        auto d{ digits.cbegin() };
        for (auto i{ length }; i--; ) {
            d = std::max_element(d, digits.cend() - i);
            result.push_back(*d++);
        }
    }
    if (!length)
        result = "ERROR: length == 0";
    else if (result.empty())
        result = "ERROR: input string does not have any digits";
    else if (require_exact_length && result.size() < length)
        result = "ERROR: input string does not have enough digits";
    else
        result = '\"' + result + '\"';
    return result;
}

int main()
{
    auto& log{ std::cout };
    char const delim{ '\"' };
    std::vector<std::string> test_strings
    {
        "24d5n4r05f704n652z393"
            , "pi = 3.1415926535897932384626433..."
            , "A"
            , ""
            , "1"
            , "123"
            , "1234"
    };
    log << "REQUIRE EXACT LENGTH:\n\n";
    for (auto const& s : test_strings)
    {
        log << "input  : " << delim << s << delim
            << "\noutput : " << extract_biggest_number(s)
            << "\n\n";
    }
    log << "\nALLOW SHORTER LENGTHS:\n\n";
    for (auto const& s : test_strings)
    {
        log << "input  : " << delim << s << delim
            << "\noutput : " << extract_biggest_number(s, false)
            << "\n\n";
    }
    return 0;
}

Here is the output generated by this program:

REQUIRE EXACT LENGTH:

input  : "24d5n4r05f704n652z393"
output : "7693"

input  : "pi = 3.1415926535897932384626433..."
output : "9998"

input  : "A"
output : ERROR: input string does not have any digits

input  : ""
output : ERROR: input string does not have any digits

input  : "1"
output : ERROR: input string does not have enough digits

input  : "123"
output : ERROR: input string does not have enough digits

input  : "1234"
output : "1234"


ALLOW SHORTER LENGTHS:

input  : "24d5n4r05f704n652z393"
output : "7693"

input  : "pi = 3.1415926535897932384626433..."
output : "9998"

input  : "A"
output : ERROR: input string does not have any digits

input  : ""
output : ERROR: input string does not have any digits

input  : "1"
output : "1"

input  : "123"
output : "123"

input  : "1234"
output : "1234"
tbxfreeware
  • 547
  • 1
  • 9
0

Somebody else will have to decide what to do about less than 4 available digits, or if there is a 0 at the front. Either way, you could make that decision after the function call.

#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;

//--------------------------------------

string best_n( const string &input, int n )
{
   string digits, best;
   for ( char c : input ) if ( isdigit( c ) ) digits += c;
   if ( digits.size() <= n ) return digits;
   auto e = digits.begin();
   while ( n-- )
   {
      e = max_element( e, digits.end() - n );
      best += *e++;
   }
   return best;
}

//--------------------------------------

int main()
{
   string tests[] = { "24d5n4r05f704n652z393", "abc", "1a9b3c", "3.1415926535897932384626433", "1234", "0a1b2c3" };
   for ( const string &s : tests ) cout << s << " -----> " << best_n( s, 4 ) << '\n';
}

Output:

24d5n4r05f704n652z393 -----> 7693
abc -----> 
1a9b3c -----> 193
3.1415926535897932384626433 -----> 9998
1234 -----> 1234
0a1b2c3 -----> 0123
lastchance
  • 1,436
  • 1
  • 3
  • 12