Как мне искать двумерный массив в любом направлении - PullRequest
8 голосов
/ 05 февраля 2010

Я пишу головоломку для поиска слов в C #, и я хотел бы иметь возможность элегантно искать слова в двумерном массиве символов.

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

Вот пример головоломки, которую я пытаюсь решить, любые идеи будут высоко оценены.

BXXD
AXEX
TRXX
FXXX

Летучая мышь Фред

РЕДАКТИРОВАТЬ: Престижность Стиву за предоставленную мне идею поиска точек компаса

РЕДАКТИРОВАТЬ: В результате поиска необходимо вернуть координаты x1, y1 и x2, y2 слов в массиве.

РЕДАКТИРОВАТЬ: Спасибо Antti за предоставленный хороший алгоритм для поиска в массиве.

Это окончательный результат, который я придумал. Я основал его на алгоритме в ответе Антти, изменив его так, чтобы он возвращал смещения массива для начала и конца всех найденных слов. Этот алгоритм будет использоваться для моих детей в игре «Поиск слова», которую я пишу в WPF. Спасибо всем за помощь. Я выложу здесь ссылку на приложение, когда оно будет респектабельным.

public class Range
{
    public Range(Coordinate start, Coordinate end)
    {
        Start = start;
        End = end;
    }

    public Coordinate Start { get; set; }
    public Coordinate End { get; set; }
}

public class Coordinate
{
    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }
}

public class WordSearcher 
{
    public WordSearcher(char[,] puzzle)
    {
        Puzzle = puzzle;
    }

    public char[,] Puzzle { get; set; }

    // represents the array offsets for each
    // character surrounding the current one
    private Coordinate[] directions = 
    {
        new Coordinate(-1, 0), // West
        new Coordinate(-1,-1), // North West
        new Coordinate(0, -1), // North
        new Coordinate(1, -1), // North East
        new Coordinate(1, 0),  // East
        new Coordinate(1, 1),  // South East
        new Coordinate(0, 1),  // South
        new Coordinate(-1, 1)  // South West
    };

    public Range Search(string word)
    {
        // scan the puzzle line by line
        for (int y = 0; y < Puzzle.GetLength(0); y++)
        {
            for (int x = 0; x < Puzzle.GetLength(1); x++)
            {
                if (Puzzle[y, x] == word[0])
                {
                    // and when we find a character that matches 
                    // the start of the word, scan in each direction 
                    // around it looking for the rest of the word
                    var start = new Coordinate(x, y);
                    var end = SearchEachDirection(word, x, y);
                    if (end != null)
                    {
                        return new Range(start, end);
                    }
                }
            }
        }
        return null;
    }

    private Coordinate SearchEachDirection(string word, int x, int y)
    {
        char[] chars = word.ToCharArray();
        for (int direction = 0; direction < 8; direction++)
        {
            var reference = SearchDirection(chars, x, y, direction);
            if (reference != null)
            {
                return reference;
            }
        }
        return null;
    }

    private Coordinate SearchDirection(char[] chars, int x, int y, int direction)
    {
        // have we ve moved passed the boundary of the puzzle
        if (x < 0 || y < 0 || x >= Puzzle.GetLength(1) || y >= Puzzle.GetLength(0))
            return null;

        if (Puzzle[y, x] != chars[0])
            return null;

        // when we reach the last character in the word
        // the values of x,y represent location in the
        // puzzle where the word stops
        if (chars.Length == 1)
            return new Coordinate(x, y);

        // test the next character in the current direction
        char[] copy = new char[chars.Length - 1];
        Array.Copy(chars, 1, copy, 0, chars.Length - 1);
        return SearchDirection(copy, x + directions[direction].X, y + directions[direction].Y, direction);
    }
}

Ответы [ 5 ]

6 голосов
/ 06 февраля 2010

НАСТОЯЩЕЕ РЕШЕНИЕ НАПИСАНО НА C ++, НО ПРИНЦИП ТО ЖЕ ВРЕМЯ

Если ваша головоломка представлена ​​

char puzzle[N][N]

объявить массивы

int xd[8] = { -1, -1,  0, +1, +1, +1,  0, -1 };
int yd[8] = {  0, -1, -1, -1,  0, +1, +1, +1 };

и затем, когда вы хотите проверить, можно ли найти слово 'w' в местоположении (x, y) в направлении d (d от 0 до 7 включительно), просто наберите

bool wordsearch(const char *w, int x, int y, int d) {
  if (*w == 0) return true; // end of word
  if (x<0||y<0||x>=N||y>=N) return false; // out of bounds
  if (puzzle[y][x] != w[0]) return false; // wrong character
  // otherwise scan forwards
  return wordsearch(w + 1, x + xd[d], y + yd[d], d); 
}

А потом драйверы

bool wordsearch(const char *w, int x, int y) {
  int d;
  for (d=0;d<8;d++)
    if (wordsearch(w, x, y, d)) return true;
  return false;
}

bool wordsearch(const char *w) {
  int x, y;
  for (x=0;x<N;x++) for(y=0;y<N;y++) if (wordsearch(w, x, y)) return true;
  return false;
}
4 голосов
/ 05 февраля 2010

Это типичная проблема, когда вы должны использовать структуру данных Trie: http://en.wikipedia.org/wiki/Trie

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

void Explore(TwoDimArray puzzle, Point2D currentCell, string currentMatch, List<string> foundSolutions);

Вы прекращаете рекурсивность, если:
- Вы нашли совпадение.
- Символ currentMatch + currentCell больше не является возможным совпадением.
- Текущая позиция ячейки больше не находится внутри области головоломки.

2 голосов
/ 22 июня 2011
public class Range
{
    public Range(Coordinate start, Coordinate end)
    {
        Start = start;
        End = end;
    }

    public Coordinate Start { get; set; }
    public Coordinate End { get; set; }
}

public class Coordinate
{
    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }
}

public class WordSearcher 
{
    public WordSearcher(char[,] puzzle)
    {
        Puzzle = puzzle;
    }

    public char[,] Puzzle { get; set; }

    // represents the array offsets for each
    // character surrounding the current one
    private Coordinate[] directions = 
    {
        new Coordinate(-1, 0), // West
        new Coordinate(-1,-1), // North West
        new Coordinate(0, -1), // North
        new Coordinate(1, -1), // North East
        new Coordinate(1, 0),  // East
        new Coordinate(1, 1),  // South East
        new Coordinate(0, 1),  // South
        new Coordinate(-1, 1)  // South West
    };

    public Range Search(string word)
    {
        // scan the puzzle line by line
        for (int y = 0; y < Puzzle.GetLength(0); y++)
        {
            for (int x = 0; x < Puzzle.GetLength(1); x++)
            {
                if (Puzzle[y, x] == word[0])
                {
                    // and when we find a character that matches 
                    // the start of the word, scan in each direction 
                    // around it looking for the rest of the word
                    var start = new Coordinate(x, y);
                    var end = SearchEachDirection(word, x, y);
                    if (end != null)
                    {
                        return new Range(start, end);
                    }
                }
            }
        }
        return null;
    }

    private Coordinate SearchEachDirection(string word, int x, int y)
    {
        char[] chars = word.ToCharArray();
        for (int direction = 0; direction < 8; direction++)
        {
            var reference = SearchDirection(chars, x, y, direction);
            if (reference != null)
            {
                return reference;
            }
        }
        return null;
    }

    private Coordinate SearchDirection(char[] chars, int x, int y, int direction)
    {
        // have we ve moved passed the boundary of the puzzle
        if (x < 0 || y < 0 || x >= Puzzle.GetLength(1) || y >= Puzzle.GetLength(0))
            return null;

        if (Puzzle[y, x] != chars[0])
            return null;

        // when we reach the last character in the word
        // the values of x,y represent location in the
        // puzzle where the word stops
        if (chars.Length == 1)
            return new Coordinate(x, y);

        // test the next character in the current direction
        char[] copy = new char[chars.Length - 1];
        Array.Copy(chars, 1, copy, 0, chars.Length - 1);
        return SearchDirection(copy, x + directions[direction].X, y + directions[direction].Y, direction);
    }
}
2 голосов
/ 05 февраля 2010

Сохраняйте первые буквы каждого слова, которое вы ищете, в списке или какой-либо подобной структуре данных. Поиск каждой буквы по порядку. Если это первая буква слова, которое вы ищете, найдите в каждой букве второе слово. Если вы найдете в слове вторую букву, обратите внимание на направление в текстовом объекте, который имеет перечисление направления, то есть {N = 0, NE, E, SE, S, SW, W, NW}. Затем просто следуйте этому направлению, пока не определите, что слово не найдено или найдено. Ключ к поисковому объекту - знать, сколько слов он просматривает. Так что, если вы ищете как гусеницу, так и крупный рогатый скот, если вы обнаружите, что C-A-T идет на северо-восток, это может быть либо Кроме того, если вы найдете букву F, вам нужно будет проверить все направления, потому что вы можете заставить FRIAR идти на восток, а FAT - на запад. Тогда это так же просто, как убедиться, что вы не выходите за пределы, потому что NE это X + 1 Y-1 и т. Д.

1 голос
/ 05 февраля 2010

НЕ используйте двумерный массив для головоломки. Для поиска слова NxM используйте массив (N + 2) * (M + 2). Поместите отступы из 1 символа вокруг головоломки. Таким образом, пример становится:

......
.BXXD.
.AXEX.
.TRXX.
.FXXX.
......

Где точки - это отступы, и все это на самом деле является 1d-массивом.

Называя ширину новой сетки диапазоном строк (S), теперь вы можете создать массив из 8 «векторов направления» D = [-S-1, -S, -S + 1, -1, 1, S-1, S, S + 1]. Используя это, вы можете смотреть из любой позиции в сетке Puzzle [position] на соседа в любом направлении, используя Puzzle [position + D [direction]].

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...