2010-02-05 43 views
7

Tôi đang viết một câu đố tìm kiếm từ trong C# và tôi muốn tìm kiếm mảng ký tự hai chiều cho các từ theo cách thanh lịch.Làm cách nào để tìm kiếm mảng hai chiều theo bất kỳ hướng nào

Các tìm kiếm cơ bản từ trái sang phải, từ trên xuống dưới, không khó để viết, tuy nhiên mọi thứ bắt đầu nhận được một chút tiết khi tìm kiếm theo đường chéo qua mảng. Tôi đã có nó làm việc nhưng tôi chắc chắn có một giải pháp tốt hơn ra khỏi đó.

Đây là ví dụ về câu đố tôi đang cố giải quyết, mọi ý tưởng sẽ được đánh giá cao.

BXXD
AXEX
TRXX
FXXX

BAT FRED

EDIT: Kudos để Steve đã cho tôi ý tưởng tìm kiếm điểm la bàn

EDIT: Kết quả của tìm kiếm cần trả về toạ độ x1, y1 và x2, y2 của các từ trong ar cá đuối.

EDIT: Nhờ Antti để cung cấp thuật toán tốt để tìm kiếm mảng.

Kết quả cuối cùng mà tôi đã đưa ra. Tôi đã dựa trên thuật toán trong câu trả lời của Antti, đã sửa đổi nó để trả về bù đắp mảng cho đầu và cuối của bất kỳ từ nào được tìm thấy. Thuật toán này sẽ được sử dụng một trò chơi Word Search tôi đang viết trong WPF, cho trẻ em của tôi. Cảm ơn tất cả mọi người đã giúp tôi. Tôi sẽ đăng một liên kết ở đây đến ứng dụng khi nó đáng kính.

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

Nếu bạn thấy phương pháp hiện tại của bạn một người nào đó có thể có một phương pháp tốt hơn. Cho đến khi chúng tôi biết làm thế nào bạn hiện đang làm nó, chúng tôi thực sự không thể cải thiện nó. – gingerbreadboy

+0

Nó dài và xấu xí và không thực sự đáng để dành thời gian để phê bình. Tôi đã hy vọng một người nào đó có kiến ​​thức tốt hơn về một cấu trúc dữ liệu và thuật toán hơn tôi sẽ có thể chỉ cho tôi đi đúng hướng. –

Trả lời

5

giải pháp này được viết bằng C++ nhưng nguyên tắc là như nhau

Nếu câu đố của bạn được đại diện bởi

char puzzle[N][N] 

khai báo mảng

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

và sau đó khi bạn muốn kiểm tra nếu từ 'w' có thể được tìm thấy tại vị trí (x, y) theo hướng d (d từ 0 đến 7 bao gồm), chỉ cần

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

Và sau đó các trình điều khiển

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

Tôi sẽ thử điều này và cho bạn biết, tôi có một giải pháp ít thanh lịch hơn (xem chỉnh sửa) nhưng tôi cũng sẽ thử cái này. –

+0

Rất thông minh, khá rực rỡ thực sự. Phải mất một thời gian cho nó để bấm, nhưng tôi sẽ có thể chuyển đổi nó thành C# và có nó trả lại của xy. Đây chính xác là loại thuật toán tôi đang tìm kiếm, ngắn gọn và súc tích. Cảm ơn bạn đã giúp đỡ. Đó là một trò chơi WPF tôi đang viết cho những đứa trẻ. –

2

Lưu giữ các chữ cái đầu tiên của từng từ bạn đang tìm kiếm trong danh sách hoặc một số cấu trúc dữ liệu như vậy. Tìm kiếm mọi chữ cái theo thứ tự. Nếu đó là chữ cái đầu tiên của từ bạn đang tìm kiếm thì hãy tìm kiếm từng chữ cái xung quanh chữ cái thứ hai. Nếu bạn tìm thấy một chữ cái thứ hai trong một từ, hãy lưu ý hướng trong một đối tượng từ có enum hướng, nghĩa là {N = 0, NE, E, SE, S, SW, W, NW}. Sau đó, chỉ cần làm theo hướng đó cho đến khi bạn đã xác định từ không tìm thấy hoặc được tìm thấy. Chúng chính là đối tượng tìm kiếm để biết có bao nhiêu từ mà nó đang xem xét. Vì vậy, nếu bạn đang tìm kiếm cả thức ăn và gia súc, nếu bạn tìm thấy C-A-T đi về phía đông bắc, nó có thể là một trong hai. Ngoài ra, nếu bạn tìm thấy một F bạn sẽ cần phải chắc chắn để kiểm tra mọi hướng bởi vì bạn có thể có FRIAR đi về phía đông và FAT đi về phía tây. Sau đó, đơn giản là đảm bảo bạn không vượt quá giới hạn vì NE là X + 1 Y-1, v.v ...

+0

Ban đầu tôi cũng nghĩ đệ quy đầu tiên, vấn đề là nếu bạn viết một hàm đệ quy tìm kiếm tất cả 8 hướng, bạn có thể tìm một từ snaking theo mọi hướng và trong trường hợp xấu nhất sử dụng chữ cái một lần nữa cho các từ như racecar. Đó là lý do tại sao tôi đề nghị tìm lá thư và chọn một hướng để làm theo - để ngăn chặn điều đó. Nếu bạn sử dụng đệ quy và gửi trong một lá cờ cho dù để kiểm tra tất cả các hướng bạn về cơ bản đã đánh bại mục đích của việc sử dụng đệ quy. –

4

Đây là vấn đề điển hình khi bạn sử dụng cấu trúc dữ liệu trie: http://en.wikipedia.org/wiki/Trie

Khi bạn có từ điển với tất cả các từ mục tiêu bạn đi qua từng vị trí của mảng hai chiều và gọi hàm đệ quy mở rộng tất cả 8 cách. Một cái gì đó dọc theo dòng.

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

Quý khách dừng chân recursiveness nếu:
- Bạn tìm thấy một trận đấu.
- Ký tự hiện tạiMatch + currentCell không còn cấu thành một kết quả phù hợp.
- Vị trí hiện tại của bạn không còn nằm trong khu vực câu đố nữa.

+0

+1 cho trie, mặc dù tôi không nghĩ rằng đệ quy có ý nghĩa nhiều. Tuy nhiên, Trie có thể mạnh hơn một chút so với nhu cầu của OP. Đối với bất kỳ câu đố ô chữ nào được thiết kế cho con người, thậm chí là công việc phù hợp sẽ hoạt động. – Brian

1

KHÔNG sử dụng mảng 2 chiều cho câu đố. Để tìm kiếm từ NxM, sử dụng một mảng (N + 2) * (M + 2). Đặt đệm của 1 nhân vật tất cả các cách xung quanh câu đố của bạn. Vì vậy, ví dụ trở thành:

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

Trường hợp các khoảng thời gian là phần đệm và tất cả điều này thực sự là mảng 1d.

Gọi chiều rộng của lưới mới là khoảng hàng (S), bây giờ bạn có thể tạo một mảng gồm 8 hướng "vectơ" D = [-S-1, -S, -S + 1, -1, 1 , S-1, S, S + 1]. Sử dụng điều này, bạn có thể nhìn từ bất kỳ vị trí nào trong ô lưới Puzzle [position] đến người hàng xóm theo bất kỳ hướng nào bằng cách sử dụng Puzzle [position + D [direction]].

Vị trí của bạn tất nhiên bây giờ là một biến duy nhất thay vì một cặp tọa độ. Các padding xung quanh biên giới cho bạn biết nếu bạn đã đạt đến các cạnh của hội đồng quản trị và phải là một nhân vật mà không bao giờ được sử dụng trong nội thất của câu đố.

+0

+1 cho việc sử dụng các ký tự đệm. Thật tuyệt vời khi giảm độ phức tạp mà bạn có thể nhận được trong các thủ tục tìm kiếm bằng cách tránh kiểm tra biên. –

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); 
    } 
} 
Các vấn đề liên quan