Algorithm 列出拼图中所有单词的最快算法
我在一次采访中被问到这个问题,我对最佳答案很好奇。问题是这样的:给你一个n x n的板,上面写满了字母。一个游戏算法想要找到并列出这个棋盘上所有可能的单词,其中“单词”被定义为至少由3个字母组成的字符串,水平或垂直。最省时的方法是什么 这个问题中的“单词”不需要是字典中的真实单词。关键是尽可能快地找到所有长度可接受的字符串。我想不出任何其他的方法,除了野蛮的强制方法,它遍历电路板上的所有空间,并在该空间中找到所有以字母开头的字符串,这需要O(n^3)时间。你们会怎么做 我看到这个问题被否决了,因为人们认为没有更好的解决方案。然而,这是一个微软的面试问题,我的面试官明确地告诉我,我的答案不是最优的。让Algorithm 列出拼图中所有单词的最快算法,algorithm,search,Algorithm,Search,我在一次采访中被问到这个问题,我对最佳答案很好奇。问题是这样的:给你一个n x n的板,上面写满了字母。一个游戏算法想要找到并列出这个棋盘上所有可能的单词,其中“单词”被定义为至少由3个字母组成的字符串,水平或垂直。最省时的方法是什么 这个问题中的“单词”不需要是字典中的真实单词。关键是尽可能快地找到所有长度可接受的字符串。我想不出任何其他的方法,除了野蛮的强制方法,它遍历电路板上的所有空间,并在该空间中找到所有以字母开头的字符串,这需要O(n^3)时间。你们会怎么做 我看到这个问题被否决了,因
m(x)=max{0,x}
。如果我们使用基于0的索引,则
s(x,y) = m(x-1) + m(n-x-2) + m(y-1) + m(n-y-2)
2*n*(n-1)*(n-2)
从位置(x,y)
开始的单词。在水平方向上,左边是以列0,1,…,y-2结尾的,右边是以列x+2,x+3,…,n-1结尾的。垂直词也类似
因此,在每个位置上,从2*(n-3)
和2*(n-2)
单词(包括)之间开始
更准确地说,在位置(x,y)
,当且仅当y=0
或y=n-1
,否则n-3
字开始出现n-2
水平字。这使得2*(n-2)+(n-2)*(n-3)=(n-1)*(n-2)
每行水平单词。每列的垂直字数是相同的,因此总共有
s(x,y) = m(x-1) + m(n-x-2) + m(y-1) + m(n-y-2)
2*n*(n-1)*(n-2)
网格中不一定有不同的单词。假设字母表不太小,重复的比例平均不太大,因此不可能有一个复杂度低于O(n³)
的算法
如果不考虑重复,就这样,只剩下遍历数组的低级变化
如果应该删除重复项,并且目标是尽可能高效地列出所有不同的单词,那么问题是什么样的数据结构允许尽可能高效地删除重复项。我不能回答这个问题,但我认为trie在这方面相当有效。这里有两个问题-一个:原始的解决方案暴力不是O(n^3)
,而是O(n^4)
假设您将每个子字符串复制到列表中的一个新条目。你有O(n^3)
单词。但是,每个子字符串本身都是O(n)
(平均而言),因此将所有这些子字符串复制到列表实际上是O(n^4)
二:
更有效的解决方案是维护数据结构,并使用类似DFS的遍历从矩阵中的每个索引(从右到下)填充数据结构
这将产生O(n^3)
解决方案,用O(n^3)
单词填充trie。你为什么说O(n^3)
?电路板是正方形的,正方形是O(n^2)
代码如下:
for(int col=0; col<n-2; ++col) {
for(int row=0; row<n-2; ++row) {
// for given (row,col)
// yield word to right
// yield word down
// yield word down-right
}
}
输出
LWIDM
OWWGR
APVOM
GKECL
TXCPD
0: LWI
1: LOA
2: LWV
3: WID
4: WWP
5: WWO
6: IDM
7: IWV
8: IGM
9: OWW
10: OAG
11: OPE
12: WWG
13: WPK
14: WVC
15: WGR
16: WVE
17: WOL
18: APV
19: AGT
20: AKC
21: PVO
22: PKX
23: PEP
24: VOM
25: VEC
26: VCD
LWIDM
OWWGR
阿普沃姆
GKECL
TXCPD
0:LWI
1:LOA
2:LWV
3:WID
第4章:世界自然保护计划
5:WWO
6:IDM
7:IWV
8:IGM
9:OWW
10:OAG
11:OPE
12:WWG
13:WPK
14:WVC
15:WGR
16:WVE
17:WOL
18:APV
19:AGT
20:AKC
21:PVO
22:PKX
23:PEP
24:VOM
25:VEC
26:VCD
结果数为27=3*(5-2)^2以下是我分阶段解决问题的方法:
尝试在拼图中查找给定的单词:您可以使用DFS在拼图中查找单词。这将是O(n^2),因为我们必须遍历每一行和每一列字符
在一个拼图中找出所有给定的单词:如果有x个给定的单词,你可以对每个单词使用上面的算法。复杂度为O(x*n^2)
如果有相同前缀的单词,那么我们将重复搜索前缀所做的工作。这可以通过为给定的单词构建一个Trie结构并将Trie的DFS与拼图的DFS相结合来避免
下面是C++中第一步的大致实现:
bool FindWordInPuzzle(int i, int j, char nextChar, int nextCharId, string word, int m, int n, bool **mark, char **maze)
{
int move[8][2] = { 0, -1, -1, -1, -1, 0, -1, 1, 0, 1, 1, 1, 1, 0, 1, -1 };
mark[i][j] = 1;
for (int r = 0; r < 8; r++) {
int g = i + move[r][0];
int h = j + move[r][1];
if (g > 0 && g < m + 2 && h > 0 && h < m + 2 && mark[g][h] == 0 && maze[g][h] == nextChar) {
nextCharId++;
if (nextCharId >= word.length()) {
return true;
}
if (FindWordInPuzzle(g, h, word[nextCharId], nextCharId, word, m, n, mark, maze)) {
return true;
}
}
}
return false;
}
bool FindWord(char **maze, bool **mark, int m, int n, string word) {
char currentChar = word[0];
int currentCharId = 0;
for (int row = 1; row < m + 2; row++) {
for (int col = 1; col < n+2; col++) {
if (maze[row][col] == currentChar && mark[row][col] == 0) {
currentCharId++;
if (currentCharId >= word.length()) {
return true;
}
if (FindWordInPuzzle(row, col, word[currentCharId], currentCharId, word, m, n, mark, maze)) {
return true;
}
}
currentCharId = 0;
currentChar = word[0];
}
}
return false;
}
int main() {
string word;
int m, n;
cin >> word;
if (word.length() <= 0) return 0;
cin >> m >> n;
char** maze;
bool **mark;
// declare arrays
maze = new char*[m + 2];
mark = new bool*[m + 2];
for (int i = 0; i < m + 2; i++) {
maze[i] = new char[n + 2];
mark[i] = new bool[n + 2];
}
// boundaries
for (int i = 0; i < m + 2; i++) {
maze[0][i] = ' ';
maze[i][0] = ' ';
maze[0][m + 1] = ' ';
maze[i][m + 1] = ' ';
mark[0][i] = 1;
mark[i][0] = 1;
mark[0][m + 1] = 1;
mark[i][m + 1] = 1;
}
// get values
for (int i = 1; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
cin >> maze[i][j];
mark[i][j] = 0;
}
}
bool val = FindWord(maze, mark, m, n, word);
cout << val;
cin >> word;
return 0;
}
bool FindWordInPuzzle(int i,int j,char nextChar,int nextCharId,string word,int m,int n,bool**mark,char**maze)
{
int move[8][2]={0,-1,-1,-1,0,-1,1,0,1,1,1,1,0,1,-1};
标记[i][j]=1;
对于(int r=0;r<8;r++){
int g=i+move[r][0];
int h=j+move[r][1];
如果(g>0&&g0&&h=word.length()){
返回true;
}
if(FindWordInPuzzle(g,h,word[nextCharId],nextCharId,word,m,n,mark,maze)){
返回true;
}
}
}
返回false;
}
布尔FindWord(字符**迷宫,布尔**标记,整数m,整数n,字符串字){
char currentChar=字[0];
int currentCharId=0;
对于(int行=1;行=word.length()){
返回true;
}
if(FindWordInPuzzle(行、列、字[currentCharId]、currentCharId、字、m、n、标记、迷宫)){
返回true;
}
}
currentCharId=0;
currentChar=字[0];
}
}
返回false;
}
int main(){
字符串字;
int m,n;
cin>>单词;
if(word.length()>m>>n;
字符**迷宫;
布尔**马克;
//声明数组
迷宫=新字符*[m+2];
马克=新布尔值*[m+2];
对于(int i=0;i