Given a set of words (without duplicates), find all word squares you can build from them.

A sequence of words forms a valid word square if the kth row and column read the exact same string, where 0 ≤ k < max(numRows, numColumns).

For example, the word sequence ["ball","area","lead","lady"] forms a word square because each word reads the same both horizontally and vertically.

b a l l
a r e a
l e a d
l a d y

Note:

  1. There are at least 1 and at most 1000 words.
  2. All words will have the exact same length.
  3. Word length is at least 1 and at most 5.
  4. Each word contains only lowercase English alphabet a-z.

Example 1:

Input:
["area","lead","wall","lady","ball"] Output:
[
[ "wall",
"area",
"lead",
"lady"
],
[ "ball",
"area",
"lead",
"lady"
]
] Explanation:
The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters).

Example 2:

Input:
["abat","baba","atan","atal"] Output:
[
[ "baba",
"abat",
"baba",
"atan"
],
[ "baba",
"abat",
"baba",
"atal"
]
] Explanation:
The output consists of two word squares. The order of output does not matter (just the order of words in each word square matters).

这道题是之前那道 Valid Word Square 的延伸,由于要求出所有满足要求的单词平方,所以难度大大的增加了,不要幻想着可以利用之前那题的解法来暴力破解,OJ 不会答应的。那么根据以往的经验,对于这种要打印出所有情况的题的解法大多都是用递归来解,那么这题的关键是根据前缀来找单词,如果能利用合适的数据结构来建立前缀跟单词之间的映射,使得我们能快速的通过前缀来判断某个单词是否存在,这是解题的关键。对于建立这种映射,这里主要有两种方法,一种是利用 HashMap 来建立前缀和所有包含此前缀单词的集合之前的映射,第二种方法是建立前缀树 Trie,顾名思义,前缀树专门就是为这种问题设计的。首先来看第一种方法,用 HashMap 来建立映射的方法,就是取出每个单词的所有前缀,然后将该单词加入该前缀对应的集合中去,然后建立一个空的 nxn 的 char 矩阵,其中n为单词的长度,目标就是来把这个矩阵填满,从0开始遍历,先取出长度为0的前缀,即空字符串,由于在建立映射的时候,空字符串也和每个单词的集合建立了映射,然后遍历这个集合,用遍历到的单词的i位置字符,填充矩阵 mat[i][i],然后j从 i+1 出开始遍历,对应填充矩阵 mat[i][j] 和 mat[j][i],然后根据第j行填充得到的前缀,到哈希表中查看有没单词,如果没有,就 break 掉,如果有,则继续填充下一个位置。最后如果 j==n 了,说明第0行和第0列都被填好了,再调用递归函数,开始填充第一行和第一列,依次类推,直至填充完成,参见代码如下:

 

解法一:

class Solution {
public:
vector<vector<string>> wordSquares(vector<string>& words) {
vector<vector<string>> res;
unordered_map<string, set<string>> m;
int n = words[].size();
for (string word : words) {
for (int i = ; i < n; ++i) {
string key = word.substr(, i);
m[key].insert(word);
}
}
vector<vector<char>> mat(n, vector<char>(n));
helper(, n, mat, m, res);
return res;
}
void helper(int i, int n, vector<vector<char>>& mat, unordered_map<string, set<string>>& m, vector<vector<string>>& res) {
if (i == n) {
vector<string> out;
for (int j = ; j < n; ++j) out.push_back(string(mat[j].begin(), mat[j].end()));
res.push_back(out);
return;
}
string key = string(mat[i].begin(), mat[i].begin() + i);
for (string str : m[key]) {
mat[i][i] = str[i];
int j = i + ;
for (; j < n; ++j) {
mat[i][j] = str[j];
mat[j][i] = str[j];
if (!m.count(string(mat[j].begin(), mat[j].begin() + i + ))) break;
}
if (j == n) helper(i + , n, mat, m, res);
}
}
};

下面来看建立前缀树 Trie 的方法,这种方法的难点是看能不能熟练的写出 Trie 的定义,还有构建过程,以及后面在递归函数中,如果利用前缀树来快速查找单词的前缀,总之,这道题是前缀树的一种经典的应用,能白板写出来就说明基本上已经掌握了前缀树了,参见代码如下:

解法二:

class Solution {
public:
struct TrieNode {
vector<int> indexs;
vector<TrieNode*> children;
TrieNode(): children(, nullptr) {}
};
TrieNode* buildTrie(vector<string>& words) {
TrieNode *root = new TrieNode();
for (int i = ; i < words.size(); ++i) {
TrieNode *t = root;
for (int j = ; j < words[i].size(); ++j) {
if (!t->children[words[i][j] - 'a']) {
t->children[words[i][j] - 'a'] = new TrieNode();
}
t = t->children[words[i][j] - 'a'];
t->indexs.push_back(i);
}
}
return root;
}
vector<vector<string>> wordSquares(vector<string>& words) {
TrieNode *root = buildTrie(words);
vector<string> out(words[].size());
vector<vector<string>> res;
for (string word : words) {
out[] = word;
helper(words, , root, out, res);
}
return res;
}
void helper(vector<string>& words, int level, TrieNode* root, vector<string>& out, vector<vector<string>>& res) {
if (level >= words[].size()) {
res.push_back(out);
return;
}
string str = "";
for (int i = ; i < level; ++i) {
str += out[i][level];
}
TrieNode *t = root;
for (int i = ; i < str.size(); ++i) {
if (!t->children[str[i] - 'a']) return;
t = t->children[str[i] - 'a'];
}
for (int idx : t->indexs) {
out[level] = words[idx];
helper(words, level + , root, out, res);
}
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/425

类似题目:

Valid Word Square

参考资料:

https://leetcode.com/problems/word-squares/

https://leetcode.com/problems/word-squares/discuss/91380/java-53ms-dfs-hashmap

https://leetcode.com/problems/word-squares/discuss/91344/Short-PythonC%2B%2B-solution

https://leetcode.com/problems/word-squares/discuss/91333/Explained.-My-Java-solution-using-Trie-126ms-1616

https://leetcode.com/problems/word-squares/discuss/91337/70ms-Concise-C%2B%2B-Solution-Using-Trie-and-Backtracking

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Word Squares 单词平方的更多相关文章

  1. Leetcode: Word Squares && Summary: Another Important Implementation of Trie(Retrieve all the words with a given Prefix)

    Given a set of words (without duplicates), find all word squares you can build from them. A sequence ...

  2. [LeetCode] Word Frequency 单词频率

    Write a bash script to calculate the frequency of each word in a text file words.txt. For simplicity ...

  3. [LeetCode] Word Abbreviation 单词缩写

    Given an array of n distinct non-empty strings, you need to generate minimal possible abbreviations ...

  4. [Leetcode] word search 单词查询

    Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from l ...

  5. [Leetcode] word ladder 单词阶梯

    Given two words (start and end), and a dictionary, find the length of shortest transformation sequen ...

  6. LeetCode:Word Ladder I II

    其他LeetCode题目欢迎访问:LeetCode结题报告索引 LeetCode:Word Ladder Given two words (start and end), and a dictiona ...

  7. [leetcode]Word Ladder II @ Python

    [leetcode]Word Ladder II @ Python 原题地址:http://oj.leetcode.com/problems/word-ladder-ii/ 参考文献:http://b ...

  8. [Lintcode]Word Squares(DFS|字符串)

    题意 略 分析 0.如果直接暴力1000^5会TLE,因此考虑剪枝 1.如果当前需要插入第i个单词,其剪枝如下 1.1 其前缀(0~i-1)已经知道,必定在前缀对应的集合中找 – 第一个词填了ball ...

  9. Word Squares

    Description Given a set of words without duplicates, find all word squares you can build from them. ...

随机推荐

  1. JQuery中ajax的相关方法总结

    前提条件 话说是jquery中的ajax方法,那么前提条件当然是引入jquery啦. <script src="http://libs.baidu.com/jquery/1.9.0/j ...

  2. 【分布式】Zookeeper客户端

    一.前言 前篇博客分析了Zookeeper的序列化和通信协议,接着继续学习客户端,客户端是开发人员使用Zookeeper最主要的途径,很有必要弄懂客户端是如何与服务端通信的. 二.客户端 2.1 客户 ...

  3. Java中的泛型 (上) - 基本概念和原理

    本节我们主要来介绍泛型的基本概念和原理 后续章节我们会介绍各种容器类,容器类可以说是日常程序开发中天天用到的,没有容器类,难以想象能开发什么真正有用的程序.而容器类是基于泛型的,不理解泛型,我们就难以 ...

  4. Linux 系统命令笔记

    前言 翻出N年前学习笔记,感觉还有点用,放到博客备忘,自己查看用. 一. 系统命令笔记 1.系统 % /etc/issue           # 查看操作系统版本  %          # 观察系 ...

  5. Autofac 的属性注入,IOC的坑

    Autofac 是一款优秀的IOC的开源工具,完美的适配.Net特性,但是有时候我们想通过属性注入的方式来获取我们注入的对象,对不起,有时候你还真是获取不到,这因为什么呢? 1.你对Autofac 不 ...

  6. 前端开发:setTimeout与setInterval 定时器与异步循环数组

    前端开发:setTimeout与setInterval 定时器与异步循环数组 前言: 开通博客园三个月以来,随笔记录了工作中遇到的大大小小的难题,也看过无数篇令人启发的文章,我觉得这样的环境是极好的, ...

  7. 前端开发:css技巧,如何设置select、radio 、 checkbox 、file这些不可直接设置的样式 。

    前言: 都说程序员有三宝:人傻,钱多,死得早.博主身边的程序“猿”一大半应了这三宝,这从侧面说明了一个问题,只有理性是过不好日子的.朋友们应该把工作与生活分开,让生活变得感性,让工作变得理性,两者相提 ...

  8. [jQuery]jQuery DataTables插件自定义Ajax分页实现

    前言 昨天在博客园的博问上帮一位园友解决了一个问题,我觉得有必要记录一下,万一有人也遇上了呢. 问题描述 园友是做前端的,产品经理要求他使用jQuery DataTables插件显示一个列表,要实现分 ...

  9. html中,文件上传时使用的<input type="file">的样式自定义

    Web页面中,在需要上传文件时基本都会用到<input type="file">元素,它的默认样式: chrome下: IE下: 不管是上面哪种,样式都比较简单,和很多 ...

  10. 【.NET MF】.NET Micro Framework USB移植

    1.开发环境 windows 7  32位 MDK 4.54 .Net Micro Framework Porting Kit 4.2(RTM QFE2) .Net Micro Framework   ...