Given two sentences words1, words2 (each represented as an array of strings), and a list of similar word pairs pairs, determine if two sentences are similar.

For example, words1 = ["great", "acting", "skills"] and words2 = ["fine", "drama", "talent"] are similar, if the similar word pairs are pairs = [["great", "good"], ["fine", "good"], ["acting","drama"], ["skills","talent"]].

Note that the similarity relation is transitive. For example, if "great" and "good" are similar, and "fine" and "good" are similar, then "great" and "fine" are similar.

Similarity is also symmetric. For example, "great" and "fine" being similar is the same as "fine" and "great" being similar.

Also, a word is always similar with itself. For example, the sentences words1 = ["great"], words2 = ["great"], pairs = [] are similar, even though there are no specified similar word pairs.

Finally, sentences can only be similar if they have the same number of words. So a sentence like words1 = ["great"] can never be similar to words2 = ["doubleplus","good"].

Note:

  • The length of words1 and words2 will not exceed 1000.
  • The length of pairs will not exceed 2000.
  • The length of each pairs[i] will be 2.
  • The length of each words[i] and pairs[i][j] will be in the range [1, 20].

这道题是之前那道 Sentence Similarity 的拓展,那道题说单词之间不可传递,于是乎这道题就变成可以传递了,难度就增加了。不过没有关系,还是用经典老三样来解,BFS,DFS,和 Union Find。先来看 BFS 的解法,其实这道题的本质是无向连通图的问题,首先要做的就是建立这个连通图的数据结构,对于每个结点来说,要记录所有和其相连的结点,建立每个结点和其所有相连结点集合之间的映射,比如对于这三个相似对 (a, b), (b, c),和(c, d),我们有如下的映射关系:

a -> {b}

b -> {a, c}

c -> {b, d}

d -> {c}

那么如果要验证a和d是否相似,就需要用到传递关系,a只能找到b,b可以找到a,c,为了不陷入死循环,将访问过的结点加入一个集合 visited,那么此时b只能去,c只能去d,那么说明a和d是相似的了。用for循环来比较对应位置上的两个单词,如果二者相同,那么直接跳过去比较接下来的。否则就建一个访问即可 visited,建一个队列 queue,然后把 words1 中的单词放入 queue,建一个布尔型变量 succ,标记是否找到,然后就是传统的 BFS 遍历的写法了,从队列中取元素,如果和其相连的结点中有 words2 中的对应单词,标记 succ 为 true,并 break 掉。否则就将取出的结点加入队列 queue,并且遍历其所有相连结点,将其中未访问过的结点加入队列 queue 继续循环,参见代码如下:

解法一:

class Solution {
public:
bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<pair<string, string>> pairs) {
if (words1.size() != words2.size()) return false;
unordered_map<string, unordered_set<string>> m;
for (auto pair : pairs) {
m[pair.first].insert(pair.second);
m[pair.second].insert(pair.first);
}
for (int i = ; i < words1.size(); ++i) {
if (words1[i] == words2[i]) continue;
unordered_set<string> visited;
queue<string> q{{words1[i]}};
bool succ = false;
while (!q.empty()) {
auto t = q.front(); q.pop();
if (m[t].count(words2[i])) {
succ = true; break;
}
visited.insert(t);
for (auto a : m[t]) {
if (!visited.count(a)) q.push(a);
}
}
if (!succ) return false;
}
return true;
}
};

下面来看递归的写法,解题思路跟上面的完全一样,把主要操作都放到了一个递归函数中来写,参见代码如下:

解法二:

class Solution {
public:
bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<pair<string, string>> pairs) {
if (words1.size() != words2.size()) return false;
unordered_map<string, unordered_set<string>> m;
for (auto pair : pairs) {
m[pair.first].insert(pair.second);
m[pair.second].insert(pair.first);
}
for (int i = ; i < words1.size(); ++i) {
unordered_set<string> visited;
if (!helper(m, words1[i], words2[i], visited)) return false;
}
return true;
}
bool helper(unordered_map<string, unordered_set<string>>& m, string& cur, string& target, unordered_set<string>& visited) {
if (cur == target) return true;
visited.insert(cur);
for (string word : m[cur]) {
if (!visited.count(word) && helper(m, word, target, visited)) return true;
}
return false;
}
};

下面这种解法就是碉堡了的联合查找 Union Find 了,这种解法的核心是一个 getRoot 函数,如果两个元素属于同一个群组的话,调用 getRoot 函数会返回相同的值。主要分为两部,第一步是建立群组关系,suppose 开始时每一个元素都是独立的个体,各自属于不同的群组。然后对于每一个给定的关系对,对两个单词分别调用 getRoot 函数,找到二者的祖先结点,如果从未建立过联系的话,那么二者的祖先结点时不同的,此时就要建立二者的关系。等所有的关系都建立好了以后,第二步就是验证两个任意的元素是否属于同一个群组,就只需要比较二者的祖先结点都否相同啦。是不是有点深度学习的赶脚,先建立模型 training,然后再 test。哈哈,博主乱扯的,二者并没有什么联系。这里保存群组关系的数据结构,有时用数组,有时用 HashMap,看输入的数据类型吧,如果输入元素的整型数的话,用 root 数组就可以了,如果是像本题这种的字符串的话,需要用 HashMap 来建立映射,建立每一个结点和其祖先结点的映射。注意这里的祖先结点不一定是最终祖先结点,而最终祖先结点的映射一定是最重祖先结点,所以 getRoot 函数的设计思路就是要找到最终祖先结点,那么就是当结点和其映射结点相同时返回,否则继续循环,可以递归写,也可以迭代写,这无所谓。注意这里第一行判空是相当于初始化,这个操作可以在外面写,就是要让初始时每个元素属于不同的群组,参见代码如下:

解法三:

class Solution {
public:
bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<pair<string, string>> pairs) {
if (words1.size() != words2.size()) return false;
unordered_map<string, string> m;
for (auto pair : pairs) {
string x = getRoot(pair.first, m), y = getRoot(pair.second, m);
if (x != y) m[x] = y;
}
for (int i = ; i < words1.size(); ++i) {
if (getRoot(words1[i], m) != getRoot(words2[i], m)) return false;
}
return true;
}
string getRoot(string word, unordered_map<string, string>& m) {
if (!m.count(word)) m[word] = word;
return word == m[word] ? word : getRoot(m[word], m);
}
};

Github 同步地址:

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

类似题目:

Friend Circles

Accounts Merge

Sentence Similarity

参考资料:

https://leetcode.com/problems/sentence-similarity-ii/

https://leetcode.com/problems/sentence-similarity-ii/discuss/109747/Java-Easy-DFS-solution-with-Explanation

https://leetcode.com/problems/sentence-similarity-ii/discuss/109752/JavaC%2B%2B-Clean-Code-with-Explanation

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

[LeetCode] 737. Sentence Similarity II 句子相似度之二的更多相关文章

  1. [LeetCode] 737. Sentence Similarity II 句子相似度 II

    Given two sentences words1, words2 (each represented as an array of strings), and a list of similar ...

  2. [LeetCode] Sentence Similarity II 句子相似度之二

    Given two sentences words1, words2 (each represented as an array of strings), and a list of similar ...

  3. LeetCode 737. Sentence Similarity II

    原题链接在这里:https://leetcode.com/problems/sentence-similarity-ii/ 题目: Given two sentences words1, words2 ...

  4. [LeetCode] 734. Sentence Similarity 句子相似度

    Given two sentences words1, words2 (each represented as an array of strings), and a list of similar ...

  5. LeetCode 734. Sentence Similarity

    原题链接在这里:https://leetcode.com/problems/sentence-similarity/ 题目: Given two sentences words1, words2 (e ...

  6. [LeetCode] Number of Islands II 岛屿的数量之二

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  7. [LeetCode] Shortest Word Distance II 最短单词距离之二

    This is a follow up of Shortest Word Distance. The only difference is now you are given the list of ...

  8. [LeetCode] 685. Redundant Connection II 冗余的连接之二

    In this problem, a rooted tree is a directed graph such that, there is exactly one node (the root) f ...

  9. [LeetCode] Pascal's Triangle II 杨辉三角之二

    Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return [1,3, ...

随机推荐

  1. flink solt,并行度

    转自:https://www.jianshu.com/p/3598f23031e6 简介 Flink运行时主要角色有两个:JobManager和TaskManager,无论是standalone集群, ...

  2. LINQ 之 SelectMany

    声明:本文为www.cnc6.cn原创,转载时请注明出处,谢谢! 一.第一种用法: public static IEnumerable<TResult> SelectMany<TSo ...

  3. Winform中使用控件的Dock属性设计窗体布局,使不随窗体缩放而改变

    场景 在新建一个Winform窗体后,拖拽控件设置其布局如下 如果只是单纯的这么设计,我们在运行后,如果对窗口进行缩放就会导致如下 所以我们需要在设计页面布局时对控件进行定位设置. 注: 博客主页:h ...

  4. Java生鲜电商平台-RBAC系统权限的设计与架构

    Java生鲜电商平台-RBAC系统权限的设计与架构 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上 ...

  5. 英语Lignaloes沉香木LIGNALOES单词

    中文名沉香木 外文名Lignaloes 国内分布两广以及云南和福建等地 国外分布印度尼西亚.马来西亚.新加坡 沉香木是珍贵的香料,被用作燃烧熏香.提取香料.加入酒中,或直接雕刻成装饰品.沉香木又名沉水 ...

  6. SPC软控件提供商NWA的产品在各行业的应用(生命科学行业)

    在上一篇文章中,我们提到了NWA软件产品在各行业都有广泛的应用,并且就化工行业的应用展开了详细介绍.而在本文中,您将看到NWA产品在生命科学行业也扮演着不可替代的角色. Northwest Analy ...

  7. curl tftp libcurl 功能使用

    #include <curl/curl.h> static size_t read_callback(void *ptr, size_t size, size_t nmemb, void ...

  8. 章节十四、3-执行JavaScript命令

    一.网页页面都是由html+css和javaScript组成的,如果页面中没有javaScript的存在,就不会有操作动作的执行,例如页面上你点击的按钮或者链接都离不开javaScript.(这一节不 ...

  9. Mac启动MySQL

    启动MySQL服务 sudo /usr/local/Cellar/mysql//bin/mysql.server start 停止MySQL服务 sudo /usr/local/Cellar/mysq ...

  10. Python环境安装与基础语法(2)——数据类型、标识符、语言分类

    高级语言发展 结构化语言:以顺序(步骤化),分支,循环描述问题 面向对象语言:接近人类的认知,万物抽象成对象,对象间的关系抽象成类和继承 程序=数据结果+算法 Python解释器 CPython:由C ...