2010-02-05 6 views
7

Estoy escribiendo un acertijo de búsqueda de palabras en C# y me gustaría poder buscar la matriz bidimensional de caracteres de las palabras de una manera elegante.¿Cómo busco una matriz bidimensional en cualquier dirección?

Las búsquedas básicas de izquierda a derecha, de arriba a abajo, etc., no son difíciles de escribir, sin embargo, las cosas comienzan a ser un poco detalladas cuando se busca diagonalmente en la matriz. Lo tengo funcionando, pero estoy seguro de que hay una mejor solución por ahí.

Aquí hay un ejemplo de un rompecabezas que estoy tratando de resolver, cualquier idea sería muy apreciada.

BXXD
AXEX
TRXX
FXXX

BAT FRED

EDIT: Felicitaciones a Steve por darme la idea de buscar puntos de la brújula

EDIT: El resultado de la búsqueda necesita devolver las coordenadas x1, y1 y x2, y2 de las palabras dentro del ar rayo.

EDIT: Gracias a Antti por proporcionar un buen algoritmo para buscar la matriz.

Este es el resultado final que se me ocurrió. Lo he basado en el algoritmo de la respuesta de Antti, habiéndolo modificado para devolver las compensaciones de la matriz para el comienzo y el final de las palabras encontradas. Este algoritmo se usará en un juego de búsqueda de palabras que estoy escribiendo en WPF, para mis hijos. Gracias a todos por ayudarme. Publicaré un enlace aquí a la aplicación cuando sea respetable.

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); 
    } 
} 
+3

Si mostrar su método actual que alguien podría tener un mejor método. Hasta que sepamos cómo lo está haciendo actualmente, realmente no podemos mejorarlo. – gingerbreadboy

+0

Es largo y feo y no merece la pena criticarlo. Esperaba que alguien conociera mejor las estructuras de datos y los algoritmos que yo podría orientarme en la dirección correcta. –

Respuesta

5

esta solución está escrito en C++ pero el principio es LA MISMA

Si el puzzle está representado por

char puzzle[N][N] 

declaran las matrices

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

y luego, cuando usted quiere comprobar si la palabra 'w' se puede encontrar en la ubicación (x, y) en la dirección d (d entre 0 y 7 inclusive), simplemente haga

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

Y luego los conductores

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

Probaré esto y le haré saber que tengo una solución menos elegante en funcionamiento (ver edición) pero voy a probar esta también. –

+0

Muy inteligente, bastante brillante en realidad. Me tomó un tiempo hacer clic, pero debería poder convertirlo a C# y hacer que devuelva los xy. Este es exactamente el tipo de algoritmo que estaba buscando, breve y conciso. Gracias por tu ayuda. Es para un juego de WPF que estoy escribiendo para los niños. –

2

Guarde las primeras letras de cada palabra que busca en una lista o en alguna estructura de datos similar. Busque cada letra en orden. Si es la primera letra de una palabra que estás buscando, busca en cada letra alrededor de ella una segunda letra. Si encuentra una segunda letra en una palabra, anote la dirección en una palabra objeto que tiene una dirección enum, es decir {N = 0, NE, E, SE, S, SW, W, NW}. Luego simplemente siga esa dirección hasta que haya determinado que la palabra no se encuentra o no se encuentra. La clave es que el objeto de búsqueda sepa cuántas palabras está mirando. Por lo tanto, si busca tanto comida como ganado, si encuentra que C-A-T va hacia el noreste, podría ser cualquiera. Además, si encuentras una F, deberás asegurarte de controlar todas las direcciones porque puedes hacer que FRIAR vaya hacia el este y FAT hacia el oeste. Entonces es tan simple como asegurarse de que no van fuera de los límites debido NE es X + 1 Y-1, etc ...

+0

Originalmente pensé que era recursivo primero, el problema es que si escribes una función recursiva que busca las 8 direcciones podrías terminar encontrando una palabra serpenteando en todas las direcciones y en el peor de los casos usar letras nuevamente para palabras como racecar. Es por eso que recomendé encontrar la carta y elegir la dirección a seguir, para evitar eso. Si usas recursividad y envías una bandera para verificar todas las direcciones, básicamente has rechazado el propósito de usar la recursión. –

4

Este es el problema típico en el que se debe utilizar la estructura de datos trie: http://en.wikipedia.org/wiki/Trie

Una vez que tiene un diccionario con todas las palabras de destino, recorre cada posición de su matriz de dos dimensiones y llama a una función recursiva que expande las 8 formas. Algo parecido a.

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

dejas de recursividad si:
- Se encuentra una coincidencia.
- El carácter currentMatch + currentCell ya no constituye una posible coincidencia.
- La posición currentCell ya no está dentro del área del rompecabezas.

+0

+1 por trie, aunque no creo que la recursividad tenga mucho sentido. Sin embargo, Trie puede ser un poco más poderosa que las necesidades de OP. Para cualquier crucigrama diseñado para humanos, incluso el bruto funcionará. – Brian

1

NO utilice una matriz bidimensional para el rompecabezas. Para una búsqueda de palabras NxM, use una matriz de (N + 2) * (M + 2). Pon relleno de 1 personaje en todo tu rompecabezas. Entonces el ejemplo se convierte en:

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

Donde los períodos son el relleno y todo esto es realmente una matriz 1d.

Llamando el ancho de la nueva cuadrícula de la fila (S), ahora puede crear una matriz de 8 "vectores" de dirección D = [-S-1, -S, -S + 1, -1, 1 , S-1, S, S + 1]. Utilizando esto, puede mirar desde cualquier posición en la cuadrícula Puzzle [posición] a su vecino en cualquier dirección usando Puzzle [posición + D [dirección]].

Su posición, por supuesto, ahora es una sola variable en lugar de un par de coordenadas. El relleno alrededor del borde te dice si has llegado al borde del tablero y debería ser un personaje que nunca se usa en el interior del rompecabezas.

+0

+1 para el uso de caracteres de relleno. Es sorprendente la reducción de la complejidad que puede obtener en las rutinas de búsqueda al evitar las pruebas de límites. –

2
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); 
    } 
} 
Cuestiones relacionadas