【问题标题】:Arranging 3 letter words in a 2D matrix such that each row, column and diagonal forms a word在 2D 矩阵中排列 3 个字母的单词,使得每一行、每一列和对角线构成一个单词
【发布时间】:2011-11-29 02:54:06
【问题描述】:

给你一个包含 3 个字母单词的字典,你必须找到一个 3x3 的矩阵,使得每一行、每一列和对角线都在字典中形成一个单词。字典中的单词已排序,您可以假设从字典中检索单词的时间为 O(1)。

这是作为 Facebook 面试问题提出的。

【问题讨论】:

  • 没有对角线,听起来类似于crossword问题,即[NP-Complete](en.wikipedia.org/wiki/NP-complete)
  • 一个词可以多次使用吗?
  • 一个词只能在我不知道如何使用时使用,但我认为这可以使用动态编程和/或回溯来解决。
  • @amit:对于固定矩阵大小和固定字母表(如本例),所有可能解决方案的数量是恒定的,因此 O(1) 算法可以计算所有可能的矩阵,然后测试每一个针对字典 O(1) 时间:)
  • 它可能可以通过检查所有可能性通过回溯来解决,但运行时间将是指数的。如果这个问题确实像没有对角线的姐妹一样是 NP-Hard,那它可能是你最好的选择。

标签: string algorithm dictionary


【解决方案1】:

我的方法是首先过滤字典以创建两个新字典:第一个包含单词的所有单字母前缀(其中可能有 26 个),第二个包含单词的所有双字母前缀(其中有小于 26^2,因为没有单词以 BB 开头)。

  1. 从字典中选择一个单词,命名为X。这将是矩阵的第一行。

  2. 使用您制作的方便列表检查 X1X2X3 是否都是有效的单字母前缀。如果是,请继续第 3 步;否则返回步骤 1。

  3. 从字典中选择一个单词,命名为Y。这将是矩阵的第二行。

  4. 使用您制作的方便列表检查X1 Y1X2 Y2X3 Y3 是否都是有效的双字母前缀。如果是,继续第 5 步;否则返回第 3 步。如果这是字典中的最后一个单词,则一直返回第 1 步。

  5. 从字典中选择一个单词,命名为Z。这将是矩阵的第三行。

  6. 检查X1 Y1 Z1X2 Y2 Z2X3 Y3 Z3 是否都是字典中的单词。如果是的话,恭喜你,你做到了!否则返回第 5 步。如果这是字典中的最后一个单词,则一直返回第 3 步。

我在 Maple 中对此进行了编码,并且运行良好。我让它运行以查找所有此类矩阵,结果发现由于内存溢出,足以使 Maple 崩溃。

【讨论】:

  • 但是对角线都是有效词的情况呢?我知道你只检查列。实际上,我们最终可以检查对角线,但是如果我们按行构造矩阵,那么我们可以在添加更多行时检查矩阵。但这会使检查逻辑复杂有点
  • 还要检查 X1 Y2 并在第 4 步检查 Y2 X3 是否是后缀;在第 6 步检查对角线
【解决方案2】:

您的评论表明您也在寻找回溯解决方案,虽然效率不高,但可以解决这个问题。伪代码:

solve(dictionary,matrix):
  if matrix is full:
       if validate(dictionary,matrix) == true:
            return true
        else:
            return false
  for each word in dictionary:
      dictionary -= word
      matrix.add(word)
      if solve(dictionary,matrix) == true:
          return true
      else:
          dictionary += word
           matrix.removeLast()
   return false //no solution for this matrix.

在上面的伪代码中,matrix.add() 在第一个未占用的行中添加了给定的单词。 matrix.remove() 删除最后占用的行,validate() 检查解决方案是否合法。

激活:solve(dictionary,empty_matrix),如果算法为真,则存在一个解决方案,输入矩阵将包含它,否则它将为假。

上面的伪代码在指数时间内运行!这是非常低效的。但是,由于此问题类似于 (*) 填字游戏问题,即NP-Complete,因此它可能是您的最佳选择。

(*)原来的Crossword-Problem没有这个问题的对角条件,当然更一般:nxm矩阵,不仅仅是3x3。尽管问题相似,但我并没有想到减少,如果存在,我会很高兴看到。

【讨论】:

  • 如果你能举出一个证明填字游戏问题是 NPC(当然包括问题的确切定义),那就太好了
  • @IliaK.:这个article 在第 3.3 节讨论填字游戏问题以及如何证明它是 NPC。填字游戏问题基本上是:给定一个带有黑白方块的矩阵和一本字典,可以在其中构建一个有效的填字游戏吗?请注意,还有另一个证明,它不使用任何黑色方块,使一个完整的“白”板也是 NP-Complete
  • 在最坏的情况下它是 O(dictionary size)^3,这对于 3 个字符单词的字典来说还不错。但我认为,如果您使用修剪并逐个字符地进行修剪(从中间开始,然后尽早到角落进行最大约束),您可以很快找到解决方案。
【解决方案3】:
  • 您会发现每组唯一的 3 个单词。
  • 您会得到这 3 个单词的所有 6 个可能的矩阵。
  • 您对可以从这些矩阵(3 列和 2 个对角线)创建的 5 个单词进行字典检查。

一些 JavaScript 来说明。

//setup a test dictionary
var dict = [
 "MAD",
 "FAD",
 "CAT",
 "ADD",
 "DOG",
 "MOD",
 "FAM",
 "ADA",
 "DDD",
 "FDD"
];
for(var i=0; i<dict.length; i++)
 dict[dict[i]]=true;

// functions
function find(dict) {
for(var x=0; x<dict.length; x++) {
for(var y=x+1; y<dict.length; y++) {
for(var z=y+1; z<dict.length; z++) {
 var a=dict[x];
 var b=dict[y];
 var c=dict[z];
 if(valid(dict,a,b,c)) return [a,b,c];
 if(valid(dict,a,c,b)) return [a,c,b];
 if(valid(dict,b,a,c)) return [b,a,c];
 if(valid(dict,b,c,a)) return [b,c,a];
 if(valid(dict,c,a,b)) return [c,a,b];
 if(valid(dict,c,b,a)) return [c,b,a];
}
}
}
return null;
}
function valid(dict, row1, row2, row3) {
 var words = [];
 words.push(row1.charAt(0)+row2.charAt(0)+row3.charAt(0));
 words.push(row1.charAt(1)+row2.charAt(1)+row3.charAt(1));
 words.push(row1.charAt(2)+row2.charAt(2)+row3.charAt(2));
 words.push(row1.charAt(0)+row2.charAt(1)+row3.charAt(2));
 words.push(row3.charAt(0)+row2.charAt(1)+row1.charAt(2));
 for(var x=0; x<words.length; x++)
  if(dict[words[x]] == null) return false;
 return true;
}

//test
find(dict);

【讨论】:

  • 如果我理解伪代码,我会赞成这个,英语说得很好。
  • 函数“find”与我的前 2 个要点一致,而函数“valid”实现了第 3 个要点。伪代码是 javascript,它可以在任何带有 javascript 控制台的浏览器中调试(例如:带有 FireBug 的 FireFox)。
【解决方案4】:

我不一定要寻找回溯解决方案。让我印象深刻的是可以使用回溯,但是解决方案有点复杂。但是我们可以使用branch and bound和pruning来缩短蛮力技术。

我们不是在矩阵中搜索所有可能的组合,而是首先选择一个字符串作为最顶行。使用第一个字符,我们可以为第一列找到一个合适的竞争者。现在使用列字符串的第 2 和第 3 个字符,我们将在第二行和第三行找到合适的单词。

为了有效地查找以特定字符开头的单词,我们将使用基数排序,以便所有以特定字符开头的单词都存储在同一个列表中。这样当我们选择了矩阵的第二行和第三行时,我们就有了一个完整的矩阵。\

我们将通过检查第 2 列和第 3 列以及对角线构成字典中的单词来检查矩阵是否有效。

当我们发现矩阵有效时,我们可以停止。这有助于减少一些可能的组合。但是我觉得这可以通过考虑另一行或另一列来进一步优化,但这会有点复杂。我在下面发布了一个工作代码。

请不要介意函数的命名,因为我是一个业余编码员,我通常不会给出非常合适的名称,并且某些部分代码是硬编码为 3 个字母的单词。

#include<iostream>
#include<string>
#include<algorithm>
#include<fstream>
#include<vector>
#include<list>
#include<set>

using namespace std;

// This will contain the list of the words read from the
// input file
list<string> words[26];

// This will contain the output matrix
string out[3];

// This function finds whether the string exits
// in the given dictionary, it searches based on the 
// first character of the string

bool findString(string in)
{
    list<string> strings = words[(int)(in[0]-'a')];
    list<string>:: iterator p;

    p = find(strings.begin(),strings.end(),in);
    if(p!=strings.end())
        return true;
}

// Since we have already chosen valid strings for all the rows
// and first column we just need to check the diagnol and the 
// 2 and 3rd column

bool checkMatrix()
{
    // Diagnol 1
    string d1;
    d1.push_back(out[0][0]);
    d1.push_back(out[1][1]);
    d1.push_back(out[2][2]);

    if(!(findString(d1)))
        return false;

    // Diagnol 2
    string d2;
    d2.push_back(out[0][0]);
    d2.push_back(out[1][1]);
    d2.push_back(out[2][2]);


    if(!(findString(d2)))
        return false;

    // Column 2
    string c2;
    c2.push_back(out[0][1]);
    c2.push_back(out[1][1]);
    c2.push_back(out[2][1]);

    if(!(findString(c2)))
        return false;

    // Column 3
    string c3;
    c3.push_back(out[0][2]);
    c3.push_back(out[1][2]);
    c3.push_back(out[2][2]);


    if(!(findString(c3)))
        return false;
    else
        return true;
    // If all match then return true
}

// It finds all the strings begining with a particular character

list<string> findAll(int i)
{
    // It will contain the possible strings
    list<string> possible;
    list<string>:: iterator it;

    it = words[i].begin();
    while(it!=words[i].end())
    {
        possible.push_back(*it);
        it++;
    }

    return possible;
}

// It is the function which is called on each string in the dictionary

bool findMatrix(string in)
{
    // contains the current set of strings
    set<string> current;

    // set the first row as the input string
    out[0]=in;
    current.insert(in);

    // find out the character for the column
    char first = out[0][0];

    // find possible strings for the column
    list<string> col1 = findAll((int)(first-'a'));
    list<string>::iterator it;

    for(it = col1.begin();it!=col1.end();it++)
    {
        // If this string is not in the current set
        if(current.find(*it) == current.end())
        {
            // Insert the string in the set of current strings
            current.insert(*it);

            // The characters for second and third rows
            char second = (*it)[1];
            char third = (*it)[2];

            // find the possible row contenders using the column string
            list<string> secondRow = findAll((int)(second-'a'));
            list<string> thirdRow = findAll((int)(third-'a'));

            // Iterators
            list<string>::iterator it1;
            list<string>::iterator it2;


            for(it1= secondRow.begin();it1!=secondRow.end();it1++)
            {
                // If this string is not in the current set
                if(current.find(*it1) == current.end())
                {

                    // Use it as the string for row 2 and insert in the current set
                    current.insert(*it1);

                    for(it2 = thirdRow.begin();it2!=thirdRow.end();it2++)
                    {
                        // If this string is not in the current set
                        if(current.find(*it2) == current.end())
                        {   

                            // Insert it in the current set and use it as Row 3
                            current.insert(*it2);

                            out[1]=*it1;
                            out[2]=*it2;

                            // Check ifthe matrix is a valid matrix
                            bool result = checkMatrix();

                            // if yes the return true
                            if(result == true)
                                return result;

                            // If not then remove the row 3 string from current set
                            current.erase(*it2);
                        }
                    }
                    // Remove the row 2 string from current set
                    current.erase(*it1);
                }
            }
            // Remove the row 1 string from current set
            current.erase(*it);
        }
    }
    // If we come out of these 3 loops then it means there was no 
    // possible match for this string
    return false;           
}

int main()
{
    const char* file = "input.txt";
    ifstream inFile(file);

    string word;

    // Read the words and store them in array of lists
    // Basically radix sort them based on their first character
    // so all the words with 'a' appear in the same list 
    // i.e. words[0]

    if(inFile.is_open())
    {
        while(inFile.good())
        {
            inFile >> word;
            if(word[0] >= 'a' && word[0] <= 'z')
            {
                int index1 = word[0]-'a';
                words[index1].push_back(word);
            }
        }
    }
    else
        cout<<"The file could not be opened"<<endl;


    // Call the findMatrix function for each string in the list and
    // stop when a true value is returned

    int i;
    for(i=0;i < 26;i++)
    {
        it = words[i].begin();
        while(it!=words[i].end())
        {
            if(findMatrix(*it))
            {
                // Output the matrix
                for(int j=0;j<3;j++)
                    cout<<out[j]<<endl;

                // break out of both the loops
                i=27;
                break;
            }
            it++;
        }
    }

    // If i ==26 then the loop ran the whole time and no break was
    // called which means no match was found

    if(i==26)
        cout<<"Matrix does not exist"<<endl;

    system("pause");
    return 0;
}

我已经在一小部分字符串上测试了代码,它运行良好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    • 1970-01-01
    • 2023-02-01
    • 2012-11-09
    相关资源
    最近更新 更多