1

Тема: Допоможіть спростити код

Умова: Визначити, скільки разів у рядку зустрічається комбінація символів "С++"

#include "pch.h"
#include <iostream>
#include <string.h>
#include <clocale>
int main()
{
    setlocale(LC_CTYPE, "ukr");
    char s1[100] = "C++";
    char s2[100];
    int k = 0;
    puts("Введiть рядок символiв:");
    gets_s(s2);
    for (int i = 0;i < strlen(s2); i++)
    {
        if (s2[i] == s1[0])
        {
            for (int j = 0; j < strlen(s1); j++, i++)
            {
                if (s2[i] == s1[j])
                {
                    k++;
                }
            }
        }
    }
    k /= strlen(s1);
    if (!k)
    {
        std::cout << "Комбiнацiя 'C++' не зустрiчається жодного разу";
    }
    else
    {
        std::cout << "Комбiнацiя 'C++' зустрiчається " << k << " раз(и)";
    }
    return 0;
}

Результат:
Введiть рядок символiв:
fsdgsdC++  fgdgdC+++dg
Комбiнацiя 'C++' зустрiчається 2 раз(и)


Код працює, але підрахунок k виглядає дуже заплутано і мені здаєть, що до ранку я сам вже забуду як воно працює, тому цікаво чи можна щось спростити?

2 Востаннє редагувалося grinyuk309 (03.03.2020 23:27:30)

Re: Допоможіть спростити код

Вона працює не коректно, якщо взяти комбінацію "C+++++"
Введiть рядок символiв:
fsdfsCfg+++++gdfgdCg+++++
Комбiнацiя 'C+++++' зустрiчається 1 раз(и)

3

Re: Допоможіть спростити код

Для початку уточніть завдання. Вам заборонено використовувати стандартні функції, крім strlen? Якщо ні, то є така чудова функція strstr, що повертає вказівник на стрічку всередині іншої стрічки, вона тут може спростити код. Але, можливо, ви ще не розбиралися з вказівниками? Тоді, звісно, доведеться всі цикли писати руками.
Помилка стається через те, що цей код збільшує k на всі знайдені символи, що збігаються, а потім ділить на довжину - це неправильно, самі ж бачите. "Cx+" збільшує k на 2, хоча насправді жодного "C++" там не знайдено.

Ваша проблема в тому, що ви кодите, не сформулювавши алгоритм. Найпримітивніший алгоритм, який, я так розумію, ви намагаєтеся зробити, буде такий: для кожної позиції i в стрічці s2 перевірити, чи не починається з неї підстрічка s1, а якщо починається, то збільшити k. Підказка: "перевірка, чи підстрічка починається з..." - це внутрішній цикл і додаткова булева змінна.

Подякували: leofun01, progheheh, Eff1c3

4 Востаннє редагувалося grinyuk309 (08.03.2020 02:01:52)

Re: Допоможіть спростити код

koala написав:

Для початку уточніть завдання. Вам заборонено використовувати стандартні функції, крім strlen? Якщо ні, то є така чудова функція strstr, що повертає вказівник на стрічку всередині іншої стрічки, вона тут може спростити код. Але, можливо, ви ще не розбиралися з вказівниками? Тоді, звісно, доведеться всі цикли писати руками.
Помилка стається через те, що цей код збільшує k на всі знайдені символи, що збігаються, а потім ділить на довжину - це неправильно, самі ж бачите. "Cx+" збільшує k на 2, хоча насправді жодного "C++" там не знайдено.

Ваша проблема в тому, що ви кодите, не сформулювавши алгоритм. Найпримітивніший алгоритм, який, я так розумію, ви намагаєтеся зробити, буде такий: для кожної позиції i в стрічці s2 перевірити, чи не починається з неї підстрічка s1, а якщо починається, то збільшити k. Підказка: "перевірка, чи підстрічка починається з..." - це внутрішній цикл і додаткова булева змінна.

Я все переробив, але краще не стало
Програма нічого не виводить, якщо після С++ є ще якийсь символ. З вказівниками розбирався і можу використовувати будь-які стандартні функції.

#include "pch.h"
#include <iostream>
#include <string.h>
#include <clocale>

int main()
{
    setlocale(LC_CTYPE, "ukr");
    char s1[100] = "C++";
    char s2[100];
    int k = 0;
    puts("Введiть рядок символiв:");
    gets_s(s2);
    char *p = strstr(s2, s1);//пошук рядка s1 в s2
    while (p != NULL)//якщо рядок знайдений то:
    {
        for (int i = 0;i < isalpha(p[i]);i++)//припускаю, що помилка в умові через що виходжу за межі масиву
        {
            s2[i] = p[i + strlen(s1)];//заповнюю масив s2 стрічкою на яку вказує *р без перших strlen(s1) символів, бо припускаю, що там С++ 
        }
        p = strstr(s2, s1);//шукаю рядок s1 вже в новому масиві s2, якщо рядок не знайдено, то NULL і виводим k
        k++;
    }
    if (!k)
    {
        std::cout << "Комбiнацiя 'C++' не зустрiчається жодного разу";
    }
    else
    {
        std::cout << "Комбiнацiя 'C++' зустрiчається " << k << " раз(и)";
    }
    return 0;
}

Результат:
Введiть рядок символiв:
SAFASGFAdgsC++
Комбiнацiя 'C++' зустрiчається 1 раз(и)

Введiть рядок символiв:
dasfasfC++t

5

Re: Допоможіть спростити код

https://purecodecpp.com/uk/archives/2579

Подякували: grinyuk3091

6

Re: Допоможіть спростити код

grinyuk309 написав:

//припускаю, що помилка в умові через що виходжу за межі масиву

Навіть якщо так (а це не так), нащо вам isalpha?

grinyuk309 написав:

//заповнюю масив s2 стрічкою...

В умові було щось схоже?

Дивіться. Ви маєте довгу стрічку

PascalC++PythonJavaJ++C++CobolC++
^     ^  ^            ^
a     b  c            d

s2 вказує на позицію a в цій стрічці. p = strstr(s2,s1) буде вказувати на позицію b. Нам треба перейти на позицію d, так? strstr(s2,s1), вочевидь, знову покаже на позицію b. Але ми точно знаємо, що через strlen(s1) символів від p буде позиція c (бо ми ж знайшли входження s1). Ну так от, в циклі треба шукати p = strstr(p+strlen(s1), s1) і все, більше жодних додаткових циклів. Лише ++k.

Тепер пара слів про якість коду. Ви шукаєте щось десь. Чому ці щось і десь звуться "s1" та "s2"? "what" і "where", наприклад, значно краще підказують, що ми шукаємо "what" (що) в "where" (де). Або ж можна "haystack" (скирта сіна) і needle (голка) - від приказки "шукати голку в сіні". Все просто і зрозуміло, не те, що s1 і s2. Загальну кількість теж якось можна назвати краще, ніж "k" - скажімо, "count" (підрахунок, лік). p - в принципі, стерпно, можна вважати, що це "pointer" чи "position", хоча, звісно, якщо написати повністю, то сенс не пропаде.
Ну і функція strlen, насправді, містить цикл - вона ШУКАЄ довжину стрічки, а не просто її повертає. Тому бажано її викликати один раз і зберігати результат:

int what_len = strlen(what);
...
p = strstr(p + what_len, what);

7

Re: Допоможіть спростити код

koala написав:
grinyuk309 написав:

//припускаю, що помилка в умові через що виходжу за межі масиву

Навіть якщо так (а це не так), нащо вам isalpha?

grinyuk309 написав:

//заповнюю масив s2 стрічкою...

В умові було щось схоже?

Дивіться. Ви маєте довгу стрічку

PascalC++PythonJavaJ++C++CobolC++
^     ^  ^            ^
a     b  c            d

s2 вказує на позицію a в цій стрічці. p = strstr(s2,s1) буде вказувати на позицію b. Нам треба перейти на позицію d, так? strstr(s2,s1), вочевидь, знову покаже на позицію b. Але ми точно знаємо, що через strlen(s1) символів від p буде позиція c (бо ми ж знайшли входження s1). Ну так от, в циклі треба шукати p = strstr(p+strlen(s1), s1) і все, більше жодних додаткових циклів. Лише ++k.

Тепер пара слів про якість коду. Ви шукаєте щось десь. Чому ці щось і десь звуться "s1" та "s2"? "what" і "where", наприклад, значно краще підказують, що ми шукаємо "what" (що) в "where" (де). Або ж можна "haystack" (скирта сіна) і needle (голка) - від приказки "шукати голку в сіні". Все просто і зрозуміло, не те, що s1 і s2. Загальну кількість теж якось можна назвати краще, ніж "k" - скажімо, "count" (підрахунок, лік). p - в принципі, стерпно, можна вважати, що це "pointer" чи "position", хоча, звісно, якщо написати повністю, то сенс не пропаде.
Ну і функція strlen, насправді, містить цикл - вона ШУКАЄ довжину стрічки, а не просто її повертає. Тому бажано її викликати один раз і зберігати результат:

int what_len = strlen(what);
...
p = strstr(p + what_len, what);

Знаходження d було для мене головною проблемою, в майбутньому буду оголошувати змінні краще

8

Re: Допоможіть спростити код

strstr  не вистачить ?