Question

If I have the string .....ZZ..ZZ..... or .Z.1.Z.23Z.4.Z55,

Is there an easy way of shifting all Zcharacters in the string one space right of the current position?

Some additional test strings are:

  • .Z
  • Z.
  • ZZ.
  • .ZZ
  • Z
  • ZZ
  • ZZZ

I think a few of the higher voted answers to this question (including the currently accepted one) do not work on these tests.

Was it helpful?

Solution

Just iterate through the text and swap characters:

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;
}

Produces:

[~]$ gcc zshift.c && ./a.out
....Z.Z.Z..

There's a lot of discussion in the comments about a possible off-by-1 error in the above code. However, simple testing / stepping through is enough to show that this is not the case.

zshift "Z." -> ".Z"
zshift ".Z" -> "."
zshift "Z" -> ""

I think the behavior of "dropping" trailing Zs when shifting off the end of the string is sensible. After all, if you shift the bits of an integer, bits that end up outside the bounds of the integer are dropped.

If another behavior is desired -- for example, shifting only within the string -- the change to the algorithm is minimal:

temp = text[i+1];
if (temp == 0) continue;
text[i+1] = text[i];
text[i] = temp;

OTHER TIPS

Building on previously posted code here. Function gets str and strlen, overwrites str. Works also with subsequent Z. Going forward for speed improvement with subsequent 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
        }
    }
}

Note that John Millikin's solution is nicer to read and also correct.

Slight fix to the previous answer (shift to the right and assume '.' means "can move here"):

  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';
    }
  }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top