4x4 2D字符矩阵排列

时间:2013-01-16 15:13:28

标签: algorithm

我有一个4x4 2D字符数组,如下所示:

A B C D
U A L E
T S U G
N E Y I

现在,我需要找到3个字符,4个字符等的所有排列,直到10个。

因此,人们可以“找到”的一些词语是TENBALDBLUEGUYS

我确实在搜索SO和谷歌搜索,但没有具体的帮助。你能把我推向正确的方向,我应该学习哪种算法(A *也许?)。请保持温和,因为我不是算法人员(不是我们所有人(好吧,至少大多数:)),但我愿意学习,只是不知道从哪里开始。

3 个答案:

答案 0 :(得分:2)

啊,这就是游戏Boggle不是它...你不想要排列,你想要一个图表,你想在图中找到单词。

好吧,我首先将字符排列为图形节点,然后将它们连接到它们的直接邻居和对角线邻居。

现在您只想搜索图表。对于16个起始节点中的每一个,您将进行递归。当您移动到新节点时,必须将其标记为已使用,以便您无法再次移动它。当你离开一个节点(完全搜索过它)时,你将其取消标记。

我希望你看到这是怎么回事......

对于每个节点,您将访问其每个邻居并将该字符添加到字符串中。如果您已经考虑到此搜索构建了字典,则可以立即查看到目前为止您所拥有的字符是否为单词的开头。这很好地缩小了搜索范围。

我所说的字典就是你有一棵树,其节点的每个字母都有一个孩子。这些的美妙之处在于您只需要在搜索中存储您当前所在的树节点。如果你决定找到一个单词,你只需通过父节点回溯就可以确定它是哪个单词。

使用此树样式以及深度优先图搜索,您可以同时搜索所有可能的字长。这是我能想到的最有效的方式。


让我为你的图搜索写一个伪编码函数:

function FindWords( graphNode, dictNode, wordsList )
    # can't use a letter twice
    if graphNode.used then return

    # don't continue if the letter not part of any word
    if not dictNode.hasChild(graphNode.letter) then return
    nextDictNode = dictNode.getChild(graphNode.letter)

    # if this dictionary node is flagged as a word, add it to our list
    nextDictNode.isWord()
        wordsList.addWord( nextDictNode .getWord() )
    end

    # Now do a recursion on all our neighbours
    graphNode.used = true
    foreach nextGraphNode in graphNode.neighbours do
        FindWords( nextGraphNode, nextDictNode, wordsList )
    end
    graphNode.used = false
end

当然,要完全放弃一切:

foreach graphNode in graph do
    FindWords( graphNode, dictionary, wordsList )
end

剩下的就是构建图形和字典。我只记得那个字典数据结构叫什么!这是一个Trie。如果你需要更节省空间的存储,你可以压缩成Radix Tree或类似的,但到目前为止最简单(也是最快)的只是使用直接的Trie。

答案 1 :(得分:2)

由于您没有定义我在C#上实现的首选语言:

private static readonly int[] dx = new int[] { 1, 1, 1, 0, 0, -1, -1, -1 };
private static readonly int[] dy = new int[] { -1, 0, 1, 1, -1, -1, 0, 1 };

private static List<string> words;
private static List<string> GetAllWords(char[,] matrix ,int d)
{
    words = new List<string>();
    bool[,] visited = new bool[4, 4];
    char[] result = new char[d];

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            Go(matrix, result, visited, d, i, j);

    return words;
}

private static void Go(char[,] matrix, char[] result, bool[,] visited, int d, int x, int y)
{
    if (x < 0 || x >= 4 || y < 0 || y >= 4 || visited[x, y])
        return;

    if (d == 0)
    {
        words.Add(new String(result));
        return;
    }

    visited[x, y] = true;
    result[d - 1] = matrix[x, y];

    for (int i = 0; i < 8; i++)
    {
        Go(matrix, result, visited, d - 1, x + dx[i], y + dy[i]);
    }

    visited[x, y] = false;
}

获得结果的代码:

    char[,] matrix = new char[,] { { 'A', 'B', 'C', 'D' }, { 'U', 'A', 'L', 'E' }, { 'T', 'S', 'U', 'G' }, { 'N', 'E', 'Y', 'I' } };
    List<string> list = GetAllWords(matrix, 3);

将参数3更改为所需的文本长度。

答案 2 :(得分:0)

您似乎只使用4x4矩阵作为长度为16的数组。如果是这种情况,您可以尝试使用递归方法生成长度为k的排列,如下所示:

findPermutations(chars, i, highLim, downLim, candidate):
    if (i > downLim):
         print candidate
    if (i == highLim): //stop clause
         return
    for j in range(i,length(chars)):
         curr <- chars[i]
         candidate.append(curr)
         swap(chars,i,j) // make it unavailable for repicking
         findPermutations(chars,i+1,highLim,downLim,candidate)
         //clean up environment after recursive call:
         candidate.removeLast()
         swap(chars ,i, j)

想法是打印每个“候选人”,其中包含更多字符downLim(在您的情况下为3),并在您达到上限(highLim)时终止 - 在您的情况下为10。

每次,你“猜测”下一个放置的角色 - 然后将它追加到候选者,然后递归调用以找到下一个候选者。
对所有可能的猜测重复该过程。

注意有选择(10,16)* 10! +选择(9,16)* 9! + ... +选择(3,16)* 3!不同的这种排列,所以它可能是耗时的......


如果你想要有意义的单词,你需要一些字典(或从某些上下文中统计提取一个字典),以便将候选人与“真实单词”相匹配。