Написание строки по спирали - PullRequest
8 голосов
/ 25 августа 2011

Я недавно участвовал в конкурсе по программированию, спонсируемом компанией, и был один вопрос, который я не понял, что он спрашивал.

Вот вопрос:

Строка «PayPal - более быстрый и безопасный способ отправки денег» записана в спиральный рисунок по часовой стрелке внутри квадрата, начиная с верхнего левого угла: (возможно, вы захотите отобразить этот шаблон фиксированным шрифтом для лучшей читаемости).

   P A Y P A L
   F E R W A I
   A M O N Y S
   S D Y E T T
   R N E S O H
   E T S A F E

Затем прочитайте строку за строкой: PAYPALFERWAIAMONYSSDYETTRNESOHETSAFE

Напишите код, который будет принимать строку, рассчитайте минимальный квадрат, который будет содержат его и возвращают преобразованную строку:

Преобразование строки (текст строки);

пример:

    convert("paypalisthefastersaferwaytosendmoney") 
should return "paypalferwaiamonyssdyettrnesohetsafe"

Понимаете ли вы, как мы можем подойти к этой проблеме?

Ответы [ 11 ]

24 голосов
/ 25 августа 2011

Я полагаю, что вопрос, как написано, должен толковаться следующим образом:

Вам дана строка, и вы хотите записать эту строку в виде спирали в квадратную сетку. Напишите функцию, которая находит наименьший квадрат, который может содержать строку, записывает строку в сетку, закручивая ее символы вокруг сетки по часовой стрелке, и, наконец, объединяет строки вместе.

Например, строка «По спирали» будет выглядеть так:

                I N A
In a spiral ->  A L S -> INAALSRIP
                R I P

Чтобы увидеть, откуда исходит сетка, обратите внимание, что если вы читаете это так:

     I -> N -> A

               |
               v

     A -> L    S

     ^         |
     |         v

     R <- I <- P

Вы получаете обратно исходный текст, и если вы склеиваете строки «INA», «ALS» и «RIP» в одну строку, вы получаете «INAALSRIP».

Давайте рассмотрим каждую проблему отдельно. Во-первых, чтобы увидеть наименьший размер прямоугольника, который может содержать текст, вы, по сути, ищете наименьший идеальный квадрат, по крайней мере, такой же большой, как длина вашего текста. Чтобы найти это, вы можете взять квадратный корень длины вашей строки и округлить ее до ближайшего целого числа. Это дает вам измерение, которое вы хотели бы. Однако, прежде чем вы сможете это сделать, вам нужно убрать все символы пунктуации и пробела из строки (и, возможно, также цифры, в зависимости от приложения). Вы можете сделать это, пройдясь по строке и скопировав символы, которые на самом деле являются алфавитными, в новый буфер. В дальнейшем я буду считать, что вы это сделали.

Что касается того, как на самом деле заполнить сетку, есть действительно отличный способ сделать это. Интуиция заключается в следующем. Когда вы начинаете в сетке n x n, вашими единственными границами являются стены сетки. Каждый раз, когда вы идете по сетке, сбрасывая буквы и ударяетесь о стену, вы просто сбриваете строку или столбец из матрицы. Следовательно, ваш алгоритм может работать, отслеживая первый и последний допустимый столбец, а также первый и последний допустимый ряд. Затем вы идете по верхнему ряду слева направо написания символов. Когда вы закончите, вы увеличиваете первый допустимый ряд, так как вы больше ничего не можете поместить туда. Затем идите вниз по правой стороне, пока не достигнете дна. Когда вы закончите, вы также заблокируете последний столбец. Например, чтобы оглянуться назад на наш пример «По спирали», мы начнем с пустой сетки 3х3:

. . .
. . .
. . .

После того, как мы напишем первые три символа вверху, у нас останется следующее:

I N A
. . .
. . .

Теперь нам нужно записать оставшуюся часть строки в пустое пространство, начиная с правого верхнего квадрата и двигаясь вниз. Поскольку мы никогда не сможем написать обратно в верхний ряд, один из способов думать об этом - подумать о решении проблемы записи остальных символов по спирали в меньшем пространстве

. . .
. . .

Начиная с верхнего левого угла и двигаясь вниз.

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

firstRow = 0, lastRow = N - 1 // Bounds of the grid
firstCol = 0, lastCol = N - 1

dRow = 0  // Amount to move in the Y direction
dCol = 1  // Amount to move in the X direction

row = 0   // Current position
col = 0

for each character ch in the string:
    Write character ch to position (row, col).

    // See if we're blocked and need to turn.
    If (row + dRow, col + dCol) is not contained in the rectangle [firstRow, lastRow] x [firstCol, lastCol]:
        // Based on which way we are currently facing, adjust the bounds of the world.
        If moving left,  increment firstRow
        If moving down,  decrement lastCol
        If moving right, decrement lastRow
        If moving up,    increment firstCol

        Rotate 90 degrees

    // Finally, move forward a step.
    row += dRow
    col += dCol

Вы можете реализовать поворот на девяносто градусов с помощью трюка из линейной алгебры: чтобы повернуть вектор на 90 градусов влево, вы умножаете его на матрицу вращения

|  0   1 |
| -1   0 |

Итак, ваши новые dy и dx задаются как

|dCol'| = |  0   1 | dCol = |-dRow|
|dRow'|   | -1   0 | dRow   | dCol|

Так что вы можете повернуть налево, вычисляя

temp = dCol;
dCol = -dRow;
dRow = temp;

В качестве альтернативы, если вы точно знаете, что символ с нулевым числовым значением никогда не появляется в строке, вы можете использовать тот факт, что Java инициализирует все массивы для хранения нулей повсюду. Затем вы можете рассматривать 0 как часового, что означает «продолжать движение вперед безопасно». Эта версия (псевдо) кода будет выглядеть следующим образом:

dRow = 0  // Amount to move in the X direction
dCol = 1  // Amount to move in the Y direction

row = 0   // Current position
col = 0

for each character ch in the string:
    Write character ch to position (row, col).
    If (row + dRow, col + dCol) is not contained in the rectangle [0, 0] x [n-1, n-1]
             -or-
       The character at [row + dRow, col + dCol] is not zero:
        Rotate 90 degrees

   // Move forward a step
   row += dRow
   col += dCol

Наконец, после того как вы записали строку в спираль, вы можете преобразовать этот спиральный текст обратно в строку, проходя по строкам по одному и объединяя все найденные символы.

EDIT : Как указывает @Voo, вы можете упростить последний шаг этого алгоритма, вообще не создавая многомерный массив, а вместо этого кодируя многомерный массив как одномерный массив. Это распространенный (и умный!) Трюк. Предположим, например, что у нас есть сетка, подобная этой:

 0  1  2
 3  4  5
 6  7  8

Тогда мы можем представить это с помощью одномерного массива как

 0  1  2  3  4  5  6  7  8

Идея состоит в том, что, учитывая пару (row, col) в сетке N x N, мы можем преобразовать эту координату в соответствующее местоположение в линеаризованном массиве, посмотрев на строку позиции * N + col. Интуитивно это говорит о том, что каждый шаг в направлении y, который вы делаете, эквивалентен пропуску всех N элементов одной строки, и каждый горизонтальный шаг просто перемещает один шаг по горизонтали в линеаризованном представлении.

Надеюсь, это поможет!

2 голосов
/ 25 августа 2011

Я написал реализацию на Python, которая, я думаю, демонстрирует рабочий подход. Несмотря на то, что вы пометили свой вопрос Java, иногда я думаю, что было бы разумно создать прототип и узнать о проблемах, особенно «вопросы об интервью» на динамическом языке очень высокого уровня. Сам язык похож на псевдокод, который работает, и это помогает вам понять форму проблемы или форму вопроса.

Подсказки в том, как человек задал вопрос:

  • Есть симпатичная коробка, похожая на форму букв. Хорошее место для начала - спросите себя, как мне написать код, который делает коробку букв.
  • В какой структуре данных (массив с поиском x + (y * c) или 2d) я буду хранить коробку с буквами.
  • Как их вставить и как их достать?

С того момента, когда вы разбиваете его на более мелкие части, вы должны понимать вопрос, а затем начинать формулировать ответ.

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

from math import *
import pprint

directions = [ (0,1),(1,0),(0,-1),(-1,0) ]  

def store_spiral(array,s,l):
    direction = 0
    dx = directions[direction][0]
    dy = directions[direction][1]
    x=0
    y=0
    for c in s:
        array[x][y] = c
        x = x +dx
        y = y +dy
        if (x >= l) or (y >= l) or (x<0) or (y<0):
            x=x-dx
            y=y-dy
            direction = (direction + 1) % 4
            dx = directions[direction][0]
            dy = directions[direction][1]            
            x=x+dx
            y=y+dy
        elif (array[x][y]!=''):
            x=x-dx
            y=y-dy
            direction = (direction + 1) % 4
            dx = directions[direction][0]
            dy = directions[direction][1]  
            x=x+dx
            y=y+dy


def convert(s):
    l = len(s)
    sl = int(ceil(sqrt(l)))
    # make empty 2d array of [ ['','', ... ], .... ]
    ar2 = [['' for i in range(sl)] for j in range(sl)]
    store_spiral(ar2,s,sl)
    x=''
    for ar in ar2:
        for l in ar:
            x=x+l
    return x

a = convert("paypalisthefastersaferwaytosendmoney")

print a

А вот и идея, как сделать более холодную версию, но для этого потребуется генерировать серию значений, называемых здесь «пределами», то есть «длиной прогулки до поворота».

from math import *
import pprint

# directions = East,South,West,North
directions = [ (0,1),(1,0),(0,-1),(-1,0) ]

x=0
y=-1

def store_spiral(array,s,l):
    global x
    global y
    direction = 0
    dx = directions[direction][0]
    dy = directions[direction][1]
    d=0
    n=0
    limits=[5,4,4,3,3,2,2,1,1,1,1]
    limit=limits[n]
    for c in s:
        d=d+1
        x=x+dx
        y=y+dy
        array[y+(x*l)]=c
        if d>limit and (limit>0):
            direction = (direction + 1) % 4
            dx = directions[direction][0]
            dy = directions[direction][1]
            n=n+1
            if n>=len(limits):
                break
            limit=limits[n]
            d=0        



def convert(s):
    l = len(s)
    sl = int(ceil(sqrt(l)))
    # make empty 2d array of [ ['','', ... ], .... ]
    ar = ['*' for i in range(l)]
    #try:
    store_spiral(ar,s,sl)
    #except:
    #  pass
    x=''
    n=0
    for l in ar:
            x=x+l
            print l,
            n=n+1
            if n==sl:
                n=0
                print
    return x

a = convert("paypalisthefastersaferwaytosendmoney")

print

print 'result: ',a
1 голос
/ 25 августа 2011

Размер стороны квадрата - квадратный корень квадрата, где квадрат - это длина на первом месте.

Ответ:

  • Возьмите длину как целое число
  • Найти квадратный корень длины как число с плавающей точкой, то есть корень
  • Проверка ненулевой дробной части корня
  • Если в дробной части корня есть ненулевое значение, добавьте 1 к целочисленной части корня, иначе добавьте 0
  • Результатом является целое число
  • Это число показывает, насколько велика каждая сторона вашего квадрата
  • Создание пустого квадрата размером, вычисленным
  • Заполните квадрат, посещая каждую ячейку «по спирали», начиная с верхнего левого угла
  • Утверждение, что в исходной строке ничего не осталось после завершения посещения
  • Утверждение, что оставшаяся незаполненная часть квадрата меньше (2x размер стороны - 1)
  • Пересмотрите квадрат в порядке слева направо, сверху вниз, чтобы восстановить вывод
  • Утверждение, что длина вывода равна длине ввода
  • Готово
0 голосов
/ 07 октября 2015

Расчет минимального квадрата довольно прост. Вы можете просто проверить количество символов во входной строке и минимальный размер квадрата n * n.

1*1= 1 
2*2 = 4
3*3 = 9

, чтобы вы могли легко найти n, введя формулу

n*n >= length(input string). 
0 голосов
/ 05 июня 2014

Сначала вы заполняете матрицу 6x6 точечными символами. Затем вы устанавливаете направление на 1. Затем вы меняете направление каждый раз, когда следующий символ в направлении не является точечным символом.

public class Spiral {
static String phrase="paypalisthefastersaferwaytosendmoney";
static int deltax,deltay,direction;

public static void setDelta(){
if(direction==1){
    deltax=1;
    deltay=0;
}else if(direction==2){
    deltax=0;
    deltay=1;
}else if(direction==3){
    deltax=-1;
    deltay=0;
}else if(direction==4){
    deltax=0;
    deltay=-1;
}
}

public static void main(String[] args) {
int index=0,x,y,N=6;
char[][] MATRIX=new char[N][N];
for(y=0;y<N;y++){
    for(x=0;x<N;x++) MATRIX[y][x]='.';
}
direction=1;
setDelta();

x=0;
y=0;
    while(index<phrase.length()){
        while(x<N && x>=0 && y<N && y>=0){
            MATRIX[y][x]=phrase.charAt(index);
            System.out.print(MATRIX[y][x]);
            index++;

                if(direction==1 && MATRIX[y][x+1]!='.' || x+1==N-1) break;
                if(direction==2 && MATRIX[y+1][x]!='.' && y<N-2) break;
                if(direction==3 && MATRIX[y][x-1]!='.' || x==0) break;
                if(direction==4 && MATRIX[y-1][x]!='.' && y>=0) break;

            x+=deltax;
            y+=deltay;
        }
        if(direction==4) direction=1;
        else direction++;
        setDelta();

        x+=deltax;
        y+=deltay;
    }   
}

}
0 голосов
/ 22 ноября 2013

Попробуйте это

package com.misc;

public class SprintSpiral {

    public static void main(String[] args){

        int xStart = 0;
        int xEnd   = 3;
        int yStart = 0;
        int yEnd   = 3;

        int[][] arr = new int[4][4];

        arr[0][0]=1;
        arr[1][0]=2;
        arr[2][0]=3;
        arr[3][0]=4;
        arr[0][1]=5;
        arr[1][1]=6;
        arr[2][1]=7;
        arr[3][1]=8;

        arr[0][2]=9;
        arr[1][2]=10;
        arr[2][2]=11;
        arr[3][2]=14;
        arr[0][3]=15;
        arr[1][3]=16;
        arr[2][3]=17;
        arr[3][3]=18;


        for (int i = 0; i < 16; i++) {

            for (int j = xStart; j <= xEnd; j++) {
                    System.out.println(arr[j][yStart]);
            }
            ++yStart;
            for (int j = yStart; j <= yEnd; j++) {
                    System.out.println(arr[xEnd][j]);
            }
            xEnd--;
            for (int j = xEnd; j >= xStart; j--) {
                    System.out.println(arr[j][yEnd]);
            }
            yEnd--;
            for (int j = yEnd; j >= yStart; j--) {
                    System.out.println(arr[xStart][j]);
            }
            xStart++;

        }
    }

}
0 голосов
/ 11 августа 2012

Люди используют множество вложенных циклов и операторов if в своих решениях выше.Лично я считаю более ясным думать о том, как сделать это с точки зрения:

direction
current input position
current row
current column
num rows to fill
num cols to fill

Fill right row 0 from column 0 to column 5.
Fill down column 5 from row 1 to row 4.
Fill left row 5 from column 4 to column 0
Fill up column 0 from row 4 to row 1
etc...

Это классическое рекурсивное решение, которое можно даже изменить для пула с форк-объединением, если вы действительно этого хотите.Это конкретное решение на самом деле регулирует размер выходной сетки в соответствии с входными данными, поэтому возможно, что вы можете получить 5 строк по 6 столбцов, если урежете достаточно символов на входе (хотя вы всегда можете оставить обрезку строк и просто получить квадрат смного заготовок).

public static void placeright(char output[][], char input[], int position, int row, int col, int numrows, int numcols) {
    for (int i=0;i<numcols && position < input.length;i++) {
        output[row][col+i] = input[position++];
    }
    if (position < input.length){ 
        placedown(output, input, position, row+1, col+numcols-1, numrows-1, numcols);
    }
}
public static void placedown(char output[][], char input[], int position, int row, int col, int numrows, int numcols) {
    for (int i=0;i<numrows && position < input.length;i++) {
        output[row+i][col] = input[position++];
    }
    if (position < input.length){ 
        placeleft(output, input, position, row+numrows-1, col-1, numrows, numcols-1);
    }
}

public static void placeleft(char output[][], char input[], int position, int row, int col, int numrows, int numcols) {
    for (int i=0;i<numcols && position < input.length;i++) {
        output[row][col-i] = input[position++];
    }
    if (position < input.length){ 
        placeup(output, input, position, row-1, col-numcols+1, numrows-1, numcols);
    }
}
public static void placeup(char output[][], char input[], int position, int row, int col, int numrows, int numcols) {
    for (int i=0;i<numrows && position < input.length;i++) {
        output[row-i][col] = input[position++];
    }
    if (position < input.length){ 
        placeright(output, input, position, row-numrows+1, col+1, numrows, numcols-1);
    }
}


public static void main( String[] args )
{
    String input = "paypalisthefastersaferwaytosendmoney".toUpperCase();
    char chars[] = input.toCharArray();

    int sqrtceil = (int) Math.ceil(Math.sqrt(chars.length));
    int rows = sqrtceil;
    int cols = sqrtceil;
    while (cols*(rows-1) >= chars.length) {
        rows--;
    }
    char output[][] = new char[rows][cols];

    placeright(output, chars, 0, 0, 0, rows, cols);

    for (int i=0;i<output.length;i++) {
        for (int j=0;j<output[i].length;j++) {
            System.out.print(output[i][j] + " ");
        }
        System.out.println();
    }       
}
0 голосов
/ 11 августа 2012

// в c ++

преобразование строки (const string & s) {int len ​​= s.size ();

    //base case
    if (len == 0) return string("");

    // minimum square calculation
    int i = 1;
    while (i*i < len) ++i;
    //cout << "i=" << i << endl;

    //matrix initialization
    vector<vector<char> > matrix;
    matrix.resize(i);
    for (int j = 0; j < i; ++j)
            matrix[j].resize(i);
    for (int j = 0; j < i; ++j)
            for (int k = 0; k < i; ++k)
                    matrix[j][k] = ' ';

    //logic
    int r = 0, c = 0;
    bool right = true, down = false, left = false, up = false;      
    int curr_len = 0;
    int side = i - 1;
    while (curr_len < len)
    {
            if (right)
            {
                    for (int j = 1; (j <= side) && ((curr_len+j) <= len); ++j)
                    {
                            matrix[r][c] = s[curr_len];
                            //cout << curr_len << "|" << r << "|" << c << "|" << matrix[r][c] << "\n";
                            ++c;
                            ++curr_len;
                    }

                    right = false;
                    down = true;
            }

            if (down)
            {
                    for (int j = 1; (j <= side) && ((curr_len+j) <= len); ++j)
                    {
                            matrix[r][c] = s[curr_len];
                            //cout << curr_len << "|" << r << "|" << c << "|" << matrix[r][c] << "\n";
                            ++r;
                            ++curr_len;
                    }

                    down = false;
                    left = true;
            }

            if (left)
            {
                    for (int j = 1; (j <= side) && ((curr_len+j) <= len); ++j)
                    {
                            matrix[r][c] = s[curr_len];
                            //cout << curr_len << "|" << r << "|" << c << "|" << matrix[r][c] << "\n";
                            --c;
                            ++curr_len;
                    }

                    left = false;
                    up = true;
            }

            if (up)
            {
                    for (int j = 1; (j <= side) && ((curr_len+j) <= len); ++j)
                    {
                            matrix[r][c] = s[curr_len];
                            //cout << curr_len << "|" << r << "|" << c << "|" << matrix[r][c] << "\n";
                            --r;
                            ++curr_len;
                    }

                    up = false;
                    right = true;
                    side = side - 2;
                    ++r; ++c;
            }
    }

    stringstream ss;

    for (int j = 0; j < i; ++j)
    {
            for (int k = 0; k < i; ++k)
            {
                    ss << matrix[j][k];
            }
    }

    return ss.str();

}

0 голосов
/ 25 августа 2011

Это то, что я понимаю в вопросе.

Допустим, у нас есть строка «Привет, мир, это был первый скрипт, который я запрограммировал, как ты сегодня хорош»

Эта строка имеет64 символа.

Теперь, если вы посмотрите на строку, которую вы дали, у нее есть 36 символов, из которых квадратный корень равен 6, следовательно, 6 букв на каждой стороне.

Таким образом, строка выше имеет64 символа, квадратный корень которых равен: 8

, что означает, что минимальный квадрат должен быть равен 8 буквам с каждой стороны.

Этот ответ связан с процессом «вычислите минимальный размер»квадратное требование.

0 голосов
/ 25 августа 2011

Похоже, PAYPALISTHEFASTERSAFERWAYTOSENDMONEY - это ввод, а

P A Y P A L
F E R W A I
A M O N Y S
S D Y E T T
R N E S O H
E T S A F E

- это вывод для меня ..

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

convert(input):
  spiral(out[][],input,0,0,sqrt(input.len))
  return out.toString()

spiral(out[][],input,ix,iy,size)
  if size>0
    //calculate the frame coords with starting indices ix,iy & size of the frame
    place first 4*(size-1) chars on a frame on the ´out´ matrix
    //recursive call to create inner frame
    spiral(out,input.remainingString(),ix+1,iy+1,size-2)
  else return

и реализация в java:

public class PayPal {

    private enum Dir {

        RIGHT, DOWN, LEFT, UP;
    }

    public String convert(String input) {
        double dRoot = Math.sqrt(input.length());
        int root;
        if (Double.compare(dRoot, (int) dRoot) == 0) {
            root = (int) dRoot;
        } else {
            root = (int) dRoot + 1;
        }

        char[][] out = new char[root][root];

        spiral(out, 0, 0, root, input);
        StringBuilder sb = new StringBuilder();

        for (char[] line : out) {
            sb.append(line);
        }

        return sb.toString();
    }

    private void spiral(char[][] out, int i, int j, int size, String input) {
        Dir direction = Dir.RIGHT;

        if (size > 0) {
            if (size == 1) {
                out[i][j] = input.charAt(0);
            } else {
                for (int k = 0; k < 4 * (size - 1); k++) {
                    int di = (k != 0 && k % (size - 1) == 0 ? size - 1 : k % (size - 1));
                    switch (direction) {
                        case RIGHT:
                            out[i][j + di] = input.charAt(k);
                            break;
                        case DOWN:
                            out[i + di][j + size - 1] = input.charAt(k);
                            break;
                        case LEFT:
                            out[i + size - 1][j + size - 1 - di] = input.charAt(k);
                            break;
                        case UP:
                            out[i + size - 1 - di][j] = input.charAt(k);
                            break;
                    }
                    if (k != 0 && (k % (size - 1) == 0)) //Change direction
                    {
                        direction = Dir.values()[direction.ordinal() + 1];
                    }
                }
            }
            spiral(out, i + 1, j + 1, size - 2, input.substring(4 * (size - 1)));
        } else {
            return;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...