#include <bits/stdc++.h>
using namespace std;
int main() {
string a,b; cin >> a;
b = "WUB";
int n;
while(a.find(b)==0){
a.erase(0,3);
}
while(a.find(b)>0){
n=a.find(b);
a.replace(n,3," ");
}
n=a.size();
while(a[--n]==' '){
a.erase(n,1);
}
cout<<a;
return 0;
}
По сути, мне нужно удалить WUB
из входной строки, а если он стоит между словами, то заменить его пробелом, но я получаю ошибку, упомянутую в заголовке. Комментируя поочередно разные блоки, я обнаружил, что ошибка во 2-м цикле while
.
образец ввода:
WUBWUBABCWUB WUBWEWUBAREWUBWUBTHEWUBCHAMPIONSWUBMYWUBFRIENDWUB
Можете ли вы сказать, в чем проблема и как ее решить?
Это мой первый запрос в StackOverflow, поэтому извините, если я не предоставил важную информацию, которую мне следовало иметь.
Поиск std::string::npos
Незначительный момент (очень незначительный): формально вывод в поток должен заканчиваться символом новой строки. Итак std::cout << a << '\n';
. В реальном мире это не имеет значения; ваша программа будет работать нормально (за исключением того, что некоторые люди удивляются, увидев командную строку в конце строки вывода). Это требование является пережитком времен ориентированного на записи ввода-вывода на древних мэйнфреймах, когда требовалась серьезная гимнастика для упаковки строк текста в серию записей фиксированного размера.
Примечание: прочтите Почему мне не следует #include <bits/stdc++.h>? и В чем проблема с «использованием пространства имен std;»?.
Добро пожаловать в StackOverflow. Если ответ решает вашу проблему, вы можете нажать «✔», чтобы отметить его как принятый ответ. Вы также можете проголосовать за любой полезный ответ (голосование и принятие выполняются отдельно). Смотрите здесь: Что мне делать, если кто-то отвечает на мой вопрос?.
std::string::find возвращает не int
, а std::string::size_type
, который является беззнаковым типом (для него обычно используется size_t
, но это не гарантировано).
Более того, если ему не удается найти подстроку, он возвращает специальное значение npos. Поскольку оно беззнаковое, это значение не может быть ниже 0.
По ссылке на документацию выше:
Возвращаемое значение
Позиция первого символа найденной подстроки или npos, если нет такая подстрока найдена.
Поэтому:
n
, чтобы стать std::string::size_type
:
std::string::size_type n;
while (a.find(b) > 0) { // BTW: didn't you mean `>=` here ?
К:
while (a.find(b) != std::string::npos) {
Тогда вывод для упомянутого вами ввода будет:
ABC WE ARE THE CHAMPIONS MY FRIEND
Побочные примечания:
(1) Почему мне не следует #include <bits/stdc++.h>?
(2) В чем проблема с «использованием пространства имен std;»?
Технически, string::find()
возвращает string::size_type
, который обычно, но не гарантированно, будет size_t
. Это может быть любой беззнаковый тип, который требуется реализации.
@RemyLebeau спасибо, исправлено. Также +1 за ваш ответ, поскольку он дополняет мой.
static const size_type npos = -1;
Это специальное значение, равное максимальному значению, представленному типом
size_type
. Точное значение зависит от контекста, но обычно оно используется либо как индикатор конца строки функциями, которые ожидают строковый индекс, либо как индикатор ошибки функциями, которые возвращают строковый индекс.Примечание
Хотя в определении используется -1,
size_type
— это целочисленный тип без знака, а значениеnpos
— это наибольшее положительное значение, которое оно может содержать, благодаря неявному преобразованию знака в беззнак. Это переносимый способ указать наибольшее значение любого беззнакового типа.
Ваш первый цикл while
обрабатывает npos
правильно, но только потому, что значение npos
не равно 0.
string
целиком, чтобы увидеть, начинается ли оно с подстроки. Вместо этого вы можете использовать std::string::compare() (или std::string::starts_with() в C++20 и более поздних версиях).Ваш второй цикл while
вообще не может обработать npos
, так как значение npos
больше 0, поэтому вы в конечном итоге передаете npos
в std::string::replace(), когда больше WUB
не найдено, что затем не удается, за исключением std::out_of_range
.
a.find()
дважды за итерацию цикла является излишним. Кроме того, find()
позволяет указать индекс, с которого начать поиск. Поскольку вы уже знаете индекс, в котором вы заменяете символы, вы можете начать следующий вызов find()
с этой позиции вместо того, чтобы каждый раз возвращаться к началу строки.У вас также есть еще один потенциальный доступ за пределы зоны — в третьем while
цикле. Если n
равно 0, потому что a
пусто перед циклом или становится пустым во время цикла, то вы уменьшите n
ниже 0, что приведет к очень большому положительному значению при передаче в std::string::operator[] , вызывая неопределенное поведение (поскольку operator[]
не выполняет никакой проверки границ. Вместо этого вы можете использовать std::string::at(), если хотите).
erase()
— не самый эффективный способ обрезать пробелы в конце строки, см. Как обрезать std::string? для альтернатив.С учетом сказанного, попробуйте вместо этого следующее:
#include <iostream>
#include <string>
using namespace std;
int main() {
string a, b;
cin >> a;
b = "WUB";
while (a.compare(0, b.size(), b) == 0) {
a.erase(0, b.size());
}
string::size_type n = 0;
while ((n = a.find(b, n)) != string::npos) {
a.replace(n, b.size(), " ");
++n;
}
while ((!a.empty()) && (a.back() == ' ')) {
a.erase(a.size()-1, 1);
}
cout << a;
return 0;
}
предыдущий ответ развеял мои сомнения, но спасибо за совет по оптимизации, потому что даже после исправления я получаю WA в тесте 4, по какой-то причине я не могу понять, внедрит ли это сразу. спасибо и хорошего дня. Редактирование: около третьего цикла while... тем не менее, он будет работать, потому что там есть хотя бы одно слово, не относящееся к WUB.
Отсюда:
while(a.find(b)>0)
знаете ли вы, какое значение возвращается, если подстрока не найдена?