如何在任何方向上搜索二维数组

时间:2010-02-05 15:07:34

标签: c# algorithm

我正在用C#编写一个单词搜索拼图,我希望能够以优雅的方式搜索二维字符数组。

从左到右,从上到下等基本搜索并不难写,但是当在对角线上搜索时,事情开始变得有点冗长。我已经开始工作,但我确信那里有更好的解决方案。

以下是我想要解决的难题的一个例子,任何想法都会受到高度赞赏。

BXXD
AXEX
TRXX
FXXX

BAT FRED

编辑:感谢史蒂夫给我搜索罗盘点的想法

编辑:搜索结果需要返回数组中单词的x1,y1和x2,y2坐标。

编辑:感谢Antti为搜索数组提供了一个很好的算法。

这是我想出的最终结果。我基于Antti的答案中的算法,修改它以返回任何单词的开头和结尾的数组偏移量。这个算法将用于我在WPF中为我的孩子写的Word Search游戏。感谢大家帮助我。当它受到尊重时,我会在这里发布一个链接到该应用程序。

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 个答案:

答案 0 :(得分:6)

这个解决方案是用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 };

然后当你想检查在方向d(d和0之间的d)的位置(x,y)是否可以找到单词'w'时,只需执行

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

答案 1 :(得分:4)

这是您应该使用特里数据结构的典型问题:http://en.wikipedia.org/wiki/Trie

一旦你有一个包含所有目标词的字典,你就会遍历二维数组的每个位置,并调用一个递归函数来扩展所有8种方法。有点像。

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

如果出现以下情况,则停止递归:
- 你找到一个匹配 - currentMatch + currentCell的字符不再构成可能的匹配 - currentCell位置不再位于拼图区域内。

答案 2 :(得分:2)

在列表或某些此类数据结构中保留您要查找的每个单词的首字母。按顺序搜索每个字母。如果它是您要搜索的单词的第一个字母,则搜索其周围的每个字母以获取第二个字母。如果在单词中找到第二个字母,则记下单词对象中具有方向枚举的方向,即{N = 0,NE,E,SE,S,SW,W,NW}。然后只需按照该方向,直到您确定找不到或找到该单词。它们的关键是让搜索对象知道它正在查看多少个单词。因此,如果你正在寻找cater和牛,如果你发现C-A-T向东北方向移动,它可能也是。此外,如果你找到一个F,你需要确保检查每个方向,因为你可以让FRIAR向东,FAT向西。然后它就像确保你不出界一样简单,因为NE是X + 1 Y-1等......

答案 3 :(得分: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);
    }
}

答案 4 :(得分:1)

不要使用二维数组来拼图。对于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 + D [direction]]从任意方向的网格拼图[position]中的任意位置查看其邻居。

您的位置当然是单个变量而不是一对坐标。边框周围的填充物告诉您是否到达了棋盘的边缘,并且应该是一个从未在拼图内部使用过的角色。