LeetCode # 737 Sentence Similarity II

本文介绍了一种通过构建无向图和使用并查集算法来判断两个句子是否相似的方法。利用DFS遍历图寻找路径,或通过并查集判断单词是否属于同一类,以此来确定句子的相似性。

题目

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:

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

解题思路

DFS

本题可以采用 DFS 的方法,把 pairs 数组中的每一对相似的字符串视为无向图上的两个端点,分别建立 pairs[i].first --> pairs[i].secondpairs[i].second --> pairs[i].first 的双向边,然后遍历 words1words2[] 的每个字符串,查看是否存在一条 words[i] --> words2[i] 的路径。需要注意的是 visited 数组的设计,每次用 DFS 搜索 words1[i] 之前,要将该字符串设置为已访问,但在每次 DFS 结束后,还要将该字符串重置为未访问,这样再对下一个字符串 words1[i + 1] 进行 DFS 遍历,否则,在遍历 words1[i] 的过程中访问过的字符串在对 words1[i + 1] 进行访问时将无法访问到!

假设 Epairs 数组的长度,Vwords1words2 的长度,则 DFS 的时间复杂度是 O(V+2E) ,相当可怕的复杂度。

并查集

可以用另一种观点来看待单词的相似:如果两个单词相似,则这两个单词属于同一类,因此在遍历 words1words2 的每个单词的时候,只要判断两个单词是否属于同一类就能判断其是否相似。这样,问题就非常适合使用并查集来解决了。假设同一类的单词构成同一棵树,具体的步骤如下:

  1. 同时遍历 words1word2 中的每个单词,把每个单词的父结点设置为自己。
  2. 遍历 pairs 数组中的每一对相似单词,把第二个单词的所在树的根结点的父节点设置为第一个单词所在的树的根结点,即把两棵同类的树进行合并。
  3. 遍历 words1words2 的每个单词,分别判断对应的两个单词所在树的根结点是否相同,若遇到根结点不相同的单词,则说明两个句子不相似。

C++代码实现

DFS

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, vector<string> > dict;
        unordered_map<string, bool> visited;

        for (int i = 0; i < pairs.size(); ++i) {
            dict[pairs[i].first].push_back(pairs[i].second);
            dict[pairs[i].second].push_back(pairs[i].first);
            visited[pairs[i].first] = visited[pairs[i].second] = false;
        }
        for (int i = 0; i < words1.size(); ++i) {
            visited[words1[i]] = true;
            if (!DFS(dict, words1[i], words2[i], visited)) { return false; }
            visited[words1[i]] = false;
        }

        return true;
    }

    bool DFS(unordered_map<string, vector<string> >& dict, string src, string goal, unordered_map<string, bool>& visited) {
        if (src == goal) { return true; }
        bool flag = false;
        for (int i = 0; i < dict[src].size(); ++i) {
            if (visited[dict[src][i]]) { continue; }
            visited[dict[src][i]] = true;
            if (DFS(dict, dict[src][i], goal, visited)) { flag = true; }
            visited[dict[src][i]] = false;
            if (flag) { break; }
        }

        return flag;
    }
};

并查集

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> parents;

        for (int i = 0; i < words1.size(); ++i) { parents[words1[i]] = words1[i]; }
        for (int i = 0; i < words2.size(); ++i) { parents[words2[i]] = words2[i]; }

        for (int i = 0; i < pairs.size(); ++i) {
            string firstP = findParent(pairs[i].first, parents);
            string secondP = findParent(pairs[i].second, parents);
            parents[secondP] = firstP;
        }

        for (int i = 0; i < words1.size(); ++i) {
            if (findParent(words1[i], parents) != findParent(words2[i], parents)) {
                return false;
            }
        }

        return true;
    }

    string findParent(string son, unordered_map<string, string>& parents) {
        if (son == parents[son]) { return son; }
        parents[son] = findParent(parents[son], parents);
        return parents[son];
    }
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值