Цена - C++


#1

Имам проблем със задача на C++. В нея имаме низ от числа, букви и точки. Трябва да възтановим цената на продукт, защото принтерът се е развалил. Тя е десетично число с точка и два знака след нея.

Ето моето речение:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    bool toch=false;
    long long k;
    for(long long i = 0; i < s.size(); i++)
    {
        if(s[i]<48 || s[i]>57)
        {
            s.erase(i,1);
        }
        if(s[i] == '.')
        {
            toch = true;
            k=i+3;
        }
        else
        {
            toch = false;
        }
    }
    if(toch == false)
    {
        cout<<s<<".00"<<endl;
    }
    if(toch == true)
    {
        for(long long i = k; i < s.size(); i++)
        {
            s.erase(i,1);
        }
        cout<<s<<endl;
    }
  return 0;
}

Програмата ми не засича някои числа.
Искрено ви моля за помощ!!!


#2

Здравей, Alex,
Може ли да споделиш пълния текст на условието на задачата?


#3

Предколедно принтерът за ценови етикети в магазина се е повредил и освен цифри печата и букви, без интервали. И някъде между тях е скрита цената – десетично число с точка и два знака след нея. Ако има повече десетични точки, то само първата участва в образуването на цената. Напишете програма, която намира каква цена Х е трябвало да бъде отпечатана на етикета, ако принтерът не се беше повредил.

Вход

На единствен ред се въвежда последователност от символи, без интервали.

Изход

На единствен ред изведете пресметнатата цена Х. Отпечатайте цената с два знака след десетичната точка.

Ограничения

1 <= дължината на входната последователност <= 200.

Примери

Вход:

АеК78cvR0m1kk.7b7c89pМpp04p

Изход:

7801.77

Вход:

qweRT12ty3mkopol

Изход:

123.00


#4

Здравей отново, и благодаря за условието!

Условната конструкция в началото на първия цикъл for:

if(s[i]<48 || s[i]>57)
{
    s.erase(i,1);
}

изтрива символа с индекс i от стринга s, в случай че той е различен от цифра (в това множество влиза и десетичната точка). След тази операция на позиция i в стринга ще бъде символът, който непосредствено е следвал току що изтрития символ. Ако този символ е буква, ще се изпълни else-клаузата на следващия if, след което цикълът ще увеличи с 1 индекса i, и като следствие от това тази буква няма да бъде изтрита на следващата итерация на цикъла, тъй като на тази итерация индексът ще сочи една позиция след нея. Така при входен стринг:

AeK78cvR0m1kk.7b7c89pMpp04p

ще получим резултат:

e78v01k7789Mp04.00

Проблем имаме също и с втората условна конструкция:

if(s[i] == '.')
{
    toch = true;
    k=i+3;
}
else
{
    toch = false;
}

която би запомнила позицията на последната, вместо първата, неизтрита десетична точка от стринга, както и би задала стойност false на променливата toch, ако последният разгледан символ от стринга е различен от десетична точка.

За да отстраним тези проблеми, бихме могли да пренапишем първия цикъл във вида:

for(long long i = 0; i < s.size(); i++)
{
    if (!toch && s[i] == '.')
    {
        toch = true;
        k = i + 3;
    }
    else if (s[i] < 48 || s[i] > 57)
    {
        s.erase(i,1);
        i--;
    }
}

Тъй като вторият цикъл for има същия проблем - пропуска всеки втори символ вследствие на триенето - бихме могли да махнем увеличаването на брояча на всяка стъпка:

for (long long i = k; i < s.size(); )
{
    s.erase(i,1);
}

или, което би било по-ефективно, да заменим цикъла с

if (k < s.size()) {
    s.erase(k, s.size() - k);
}

За да обхванем и случаите, в които s съдържа десетична точка, но броят на цифрите след нея е по-малък от 2, можем да изведем крайния резултат (след като всички ненужни символи са били отстранени) с преобразуване на s към тип double и форматиране:

cout << fixed << setprecision(2) << stod(s) << endl;

Надявам, че горните забележки биха били от полза.
Поздрави,
Иван

P. S. По принцип, изтриването на елементи от някакъв контейнер (напр. стринг), докато итерираме по него, създава предпоставки за допускане на грешки. Друг подход за решаването на задачата би могъл да бъде с едно обхождане на изходния стринг, s, да копираме всички символи, които ни интересуват (цифри и точката с най-малък индекс, ако такава има) в помощен стринг, да кажем t, след което да отпечатаме необходимите символи от t.


#5

Много благодаря. Анализът на решението е много полезен.На 12 съм и още се уча.Само не ми стана ясно как програмата да намира цифрите и да ги слага в друг низ(още не съм го учил).

Благодаря много, Алекс.


#6

Извинявай, ако съм използвал някои неясни изрази! Решението с помощен стринг, което имах предвид, е следното:

int main()
{
    string s, t;
    cin >> s;
    // t.reserve(s.size());
    bool toch = false;
    int j = 0, k = 0;
    for (int i = 0; i < s.size(); i++) 
    {
        if (s[i] >= '0' && s[i] <= '9') 
        {
            t += s[i];
            j++;
        }
        if (!toch && s[i] == '.') 
        {
            t += s[i];
            j++;
            toch = true;
            k = j + 2;
        }
    }
    if (!toch) 
    {
        k = t.size();
    }
    cout << fixed << setprecision(2) << stod(t.substr(0, k)) << endl;
    return 0;
}

Със сигуност може да се направи и по-добре (например като спрем да добавяме цифри, след като срещнем две цифри след десетичната точка).

Добавянето на символа s[i] в края на стринга t става с присвояването:

t += s[i];

Същата операция може да се запише и като:

t.push_back(s[i]);

Изразът t.substr(0, k), който се подава като аргумент на функцията stod, има за стойност подстринга на t, започващ от индекс 0 и състоящ се от k на брой символа.

Поздрави и успех!
Иван

P. S. Закоментираният ред t.reserve(s.size()); не е напълно необходим. Ефектът от него би се състоял в това, в началото на програмата за все още празния стринг t да се запазят s.size() на брой байта - памет, която във всички случаи би била достатъчна за t, тъй като t е подстринг на s. Ако това не се направи, при изчерпване на заделената за t памет добавянето на символи в края на t ще довежда до ново заделяне на памет и копиране на досегашното съдържание на t (заедно с новия символ). Заради начина, по който заделянето на памет за стрингове и др. е разчетено в стандартната библиотека на C++, това ще става много по-рядко в сравнение с броя на добавените към t елементи, и при ограниченията на задачата - най-много 200 символа в s - няма да има значение.


#7

Аз ли съм единствения човек, който обожава Regular Expressions? Точно в такъв тип задачи е безценен инструмент :slight_smile:


#8

Благодаря много за подробния анализ. Откъде мога да гледам онлайн лекции С++ , защото се затруднявам с доста задачи.


#9

Съжалявам, но не мога да препоръчам конкретни онлайн лекции по C++. Това е много обширен език, от който имам само бегла представа. Когато ми трябва справка за някоя особеност на езика или за функция от стандартната библиотека, обикновено прибягвам до сайта https://en.cppreference.com/w/, но бих го препоръчал само на хора с малко повече опит. Тук https://isocpp.org/wiki/faq/how-to-learn-cpp могат да се намерят някои съвети за избор на книги (на английски). Нямам представа каква литература съществува на български език в момента. Предполагам, че задачите, които те интересуват са по-скоро от съзтезателен характер, и за тях не би трябвало да е необходимо познаване на езика в голяма дълбочина. Надявам се, че някой колега ще може да даде по-конкретен съвет.