Если у меня есть строка .....ZZ..ZZ..... или .Z.1.Z.23Z.4.Z55,
Есть ли простой способ сдвинуть все символы Z в строке на один пробел вправо от текущей позиции?
Некоторые дополнительные тестовые строки:
.ZZ.ZZ..ZZZZZZZZЯ думаю, что некоторые из наиболее высоко оцененных ответов на этот вопрос (включая принятый в настоящее время) не работают в этих тестах.
Также дайте правильный ответ для каждой тестовой строки. Простое указание на них и на то, что данные решения не работают, не очень помогает.





Просто перебирайте текст и меняйте местами символы:
int main ()
{
char text[] = "...Z.Z.Z...", temp;
int text_len = strlen (text), i;
for (i = text_len - 1; i >= 0; i--)
{
if (text[i] == 'Z')
{
temp = text[i+1];
text[i+1] = text[i];
text[i] = temp;
}
}
printf ("%s\n", text);
return 0;
}
Производит:
[~]$ gcc zshift.c && ./a.out
....Z.Z.Z..
В комментариях много говорится о возможной ошибке «off-by-1» в приведенном выше коде. Однако простого тестирования / пошагового выполнения достаточно, чтобы показать, что это не так.
zshift "Z." -> ".Z"
zshift ".Z" -> "."
zshift "Z" -> ""
Я думаю, что поведение "опускания" конечных Z при смещении с конца струны является разумным. В конце концов, если вы сдвинете биты целого числа, биты, выходящие за пределы целого числа, будут отброшены.
Если желательно другое поведение - например, сдвиг только внутри строки - изменение алгоритма минимально:
temp = text[i+1];
if (temp == 0) continue;
text[i+1] = text[i];
text[i] = temp;
@Martin: ой, ой, изменится
но что, если у вас есть 2 Z рядом друг с другом, или 3 Z, или 4 и т. д., и вы хотите сдвинуть каждую из них?
@Tomek: Если я не ошибаюсь, мой код делает это. Измените text на "..ZZ .." или что-то еще, и он выдаст правильный результат.
@John: Мне жаль, что вы были правы, я проделывал это на бумаге, и это не имело смысла, но потом я увидел, что вы начинаете с правой стороны строки, вот как все это работает. Теперь все лучше, еще раз спасибо
Это тоже неправильно, потому что, если оно заканчивается на «Z», то «Z» не будет заменено местами.
Все было так, как я сказал. Автор исправил код своим комментарием. Спасибо.
Нет, он этого не сделал, я изменил его из-за вашего комментария, а затем вернул обратно. В любом случае это неправильно
@ypnos: ошибки отклонения нет. strlen исключает терминатор NULL.
если вы начнете с ii = text_len - 1 и text [text_len-1] (последний символ перед '\ 0'!) == 'Z', то вы замените текст [textlen], который является '\ 0' , поэтому Z будет заменен на строку с завершающим нулем! Это потому что strlen исключает нулевой терминатор.
Простой тест для выяснения причины - это, конечно, увидеть, что произойдет, если строка, переданная в подпрограмму, будет просто "Z". Конечно, решить, что в таком случае должно произойти, непросто.
Я не уверен, как это должно работать, но простой тест дал мне понять, что использование ypnos fix для этого кода кажется правильным решением, поскольку измененная строка все равно будет перестановкой оригинала. Как сейчас, этого не будет.
Спасибо Андреасу за экспертную оценку. Забавно, что в коде есть явная ошибка, некоторые разработчики смотрят именно на нее и не осознают этого.
Поскольку вопрос сильно недооценен, трудно спорить, что правильно, а что неправильно, но если вы позволите Z: s сдвинуться «с» строки, вы в конечном итоге примете решение вместо этого заменить что-то другое (что?) Или обрезать строку. Мне ни один из этих двух вариантов не кажется хорошим.
> Если требуется другое поведение [snip], изменение 'text_len - 1' на text_len - 2 'решит эту проблему более аккуратно, чем добавление if (temp == 0) в цикл. Если задуматься, на самом деле эти два значения означают одно и то же.
Незначительное исправление к предыдущему ответу (сдвиньте вправо и предположите, что "." Означает "здесь можно двигаться"):
char text[] = "...Z.Z.Z...";
for (int i = strlen(text) - 2); i > 0; --i) {
if (text[i] == 'Z' && text[i + 1] == '.') {
text[i] = '.';
text[i + 1] = 'Z';
}
}
но что, если у вас есть 2 Z рядом друг с другом, или 3 Z, или 4 и т. д., и вы хотите сдвинуть каждую из них?
Я считаю, что приведенный выше код должен это сделать. Он сместит крайний правый, затем следующий крайний правый и т. д. начиная с: ZZ. Шаги приведут к: ZZ. Z.Z .ZZ
Основываясь на ранее опубликованном коде здесь. Функция получает str и strlen, перезаписывает str. Работает также с последующим Z. Дальнейшее улучшение скорости с последующим Z.
void move_z_right (char* str, int strlen) {
for (unsigned int i = 0; i < strlen - 1; ++i)
{
if (str[i] == 'Z')
{
unsigned int j = i+1;
while (str[j] == 'Z' && j < strlen - 1) ++j;
if (j == strlen) break; // we are at the end, done
char tmp = str[j];
str[j] = str[i];
str[i] = tmp;
i = j; // continue after new Z next run
}
}
}
Обратите внимание, что решение Джона Милликина удобнее читать, а также исправлять.
Вам нужно дать более точную спецификацию. Когда вы говорите «сдвинуть вправо», вы имеете в виду поменять местами букву «Z» и символ вправо? Сдвинуть его и поставить пробел (или что-то в этом роде) на то место, где оно было? Что делать, если Z - последний символ? Каков правильный результат для ваших тестовых строк?