Наложение на месте двух половин строки

Для строки четного размера, скажем:

abcdef123456

Как бы я чередовал две половины, так что одна и та же строка стала бы такой:

a1b2c3d4e5f6

Я попытался разработать алгоритм, но не смог. Кто-нибудь даст мне несколько советов о том, как действовать? Мне нужно сделать это, не создавая дополнительных строковых переменных или массивов. Одна или две переменные в порядке.

Мне просто не нужен рабочий код (или алгоритм), мне нужно разработать алгоритм и математически доказать его корректность.

Ответ 1

Возможно, вы сможете сделать это в O (N * log (N)):

Want: abcdefgh12345678 -> a1b2c3d4e5f6g7h8

a b c d e f g h
  1 2 3 4 5 6 7 8

  4 1-sized swaps:

a 1 c 3 e 5 g 7
  b 2 d 4 f 6 h 8

a1  c3  e5  g7
    b2  d4  f6  h8

  2 2-sized swaps:

a1  b2  e5  f6
    c3  d4  g7  h8

a1b2  e5f6
      c3d4  g7h8

  1 4-sized swap:

a1b2  c3d4
      e5f6  g7h8

a1b2c3d4
        e5f6g7h8

Реализация в C:

#include <stdio.h>
#include <string.h>

void swap(void* pa, void* pb, size_t sz)
{
  char *p1 = pa, *p2 = pb;
  while (sz--)
  {
    char tmp = *p1;
    *p1++ = *p2;
    *p2++ = tmp;
  }
}

void interleave(char* s, size_t len)
{
  size_t start, step, i, j;

  if (len <= 2)
    return;

  if (len & (len - 1))
    return; // only power of 2 lengths are supported

  for (start = 1, step = 2;
       step < len;
       start *= 2, step *= 2)
  {
    for (i = start, j = len / 2;
         i < len / 2;
         i += step, j += step)
    {
      swap(s + i,
           s + j,
           step / 2);
    }
  }
}

char testData[][64 + 1] =
{
  { "Aa" },
  { "ABab" },
  { "ABCDabcd" },
  { "ABCDEFGHabcdefgh" },
  { "ABCDEFGHIJKLMNOPabcdefghijklmnop" },
  { "ABCDEFGHIJKLMNOPQRSTUVWXYZ0<({[/abcdefghijklmnopqrstuvwxyz1>)}]\\" },
};

int main(void)
{
  unsigned i;

  for (i = 0; i < sizeof(testData) / sizeof(testData[0]); i++)
  {
    printf("%s -> ", testData[i]);
    interleave(testData[i], strlen(testData[i]));
    printf("%s\n", testData[i]);
  }

  return 0;
}

Выход (ideone):

Aa -> Aa
ABab -> AaBb
ABCDabcd -> AaBbCcDd
ABCDEFGHabcdefgh -> AaBbCcDdEeFfGgHh
ABCDEFGHIJKLMNOPabcdefghijklmnop -> AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPp
ABCDEFGHIJKLMNOPQRSTUVWXYZ0<({[/abcdefghijklmnopqrstuvwxyz1>)}]\ -> AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz01<>(){}[]/\

Ответ 2

В общем, эта проблема довольно сложная - и она сводится к поиску циклов перестановок. Количество и длина этих переменных сильно различаются в зависимости от длины.

Cycles for in-place interleaving for 10 and 12 entry arrays

Первый и последний циклы всегда вырождаются; 10-элементный массив имеет 2 цикла длин 6 и 2, а 12-элементный массив имеет один цикл длиной 10.

С помощью цикла:

 for (i=j; next=get_next(i) != j; i=next) swap(i,next);

Несмотря на то, что следующая функция может быть реализована как некоторая относительно простая формула N, проблема откладывается, чтобы делать бухгалтерский учет того, какие индексы были заменены. В левом случае из 10 записей следует [быстро] найти начальные позиции циклов (они, например, 1 и 3).

Ответ 3

Ok позволяет начать сначала. Вот что мы будем делать:

def interleave(string):
    i = (len(string)/2) - 1
    j = i+1

    while(i > 0):
        k = i
        while(k < j):
            tmp = string[k]
            string[k] = string[k+1]
            string[k+1] = tmp
            k+=2 #increment by 2 since were swapping every OTHER character
        i-=1 #move lower bound by one
        j+=1 #move upper bound by one

Вот пример того, что программа собирается делать. Мы будем использовать переменные i, j, k. i и j будут соответственно нижней и верхней границами, где k будет индексом, в котором мы поменяем.

Пример

`abcd1234`

i = 3 //got this from (length(string)/2) -1

j = 4 //this is really i+1 to begin with

k = 3 //k always starts off reset to whatever i is 

swap d and 1
increment k by 2 (k = 3 + 2 = 5), since k > j we stop swapping

result `abc1d234` after the first swap

i = 3 - 1 //decrement i
j = 4 + 1 //increment j
k= 2 //reset k to i

swap c and 1, increment k (k = 2 + 2 = 4), we can swap again since k < j
swap d and 2, increment k (k = 4 + 2 = 6), k > j so we stop
//notice at EACH SWAP, the swap is occurring at index `k` and `k+1`

result `ab1c2d34`

i = 2 - 1
j = 5 + 1
k = 1

swap b and 1, increment k (k = 1 + 2 = 3), k < j so continue
swap c and 2, increment k (k = 3 + 2 = 5), k < j so continue
swap d and 3, increment k (k = 5 + 2 = 7), k > j so were done

result `a1b2c3d4`

Что касается проверки правильности программы, см. ссылку . Он объясняет, как доказать, что это правильно с помощью инварианта цикла.

Грубое доказательство было бы следующим:

  • Инициализация: до первой итерации цикла мы видим, что i (length(string)/2) - 1. Мы можем видеть, что я <= length (string), прежде чем мы войдем в цикл.
  • Обслуживание. После каждой итерации i уменьшается (i = i-1, i=i-2,...) и должна быть точка, в которой i<length(string).
  • Termination: Поскольку i является уменьшающейся последовательностью положительных целых чисел, инвариант цикла i > 0 в конечном итоге будет равен false и цикл завершится.

Ответ 5

Хорошо, вот черновик. Вы говорите, что вам не нужен алгоритм, но вы принимаете намеки, поэтому рассмотрите этот алгоритм как подсказку:

Длина - N.

k = N/2 - 1.

1) Начните посередине и сдвиньте (путем последовательной замены соседних парных элементов) элемент в положении N/2 k помещается влево (первый раз: "1" переходит в положение 1).

2) - k. Is k == 0? Выход.

3) Сдвиг (путем замены) элемента в N/2 (первый раз: 'f' переходит в положение N-1) k помещается вправо.

4) --k.

Изменить: приведенный выше алгоритм верен, как показывает приведенный ниже код. Фактически доказывая, что это правильно, waaay вне моих возможностей, забавный маленький вопрос, хотя.

#include <iostream>
#include <algorithm>

int main(void)
{
    std::string s("abcdefghij1234567890");
    int N = s.size();
    int k = N/2 - 1;
    while (true)
    {

        for (int j=0; j<k; ++j)
        {
            int i = N/2 - j;
            std::swap(s[i], s[i-1]);
        }

        --k;

        if (k==0) break;

        for (int j=0; j<k; ++j)
        {
            int i = N/2 + j;
            std::swap(s[i], s[i+1]);
        }

        --k;
    }

   std::cout << s << std::endl;

   return 0;
}