Leetcode算法學習日誌-737 Sentence Similarity II

Leetcode 737 Sentence Similarity

題目原文

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].

題意分析

pairs中存儲了成對的similar words,similar關係滿足傳遞性和反向性。對於words1和words2中的每個相對應的words,如果根據pairs中的關係推斷出它們滿足similar(相同則一定similar),則說words1和word2滿足similar,返回true,否則返回false。注意如果words1和words2長度不同,則直接返回false。

解法分析

對於words1和words2相對應的一對words,判斷它們是否similar實際上是判斷它們之間是否有連通路,把一個word看做節點,該問題可以看做判斷兩點之間是否連通的圖問題。對於給定兩點,判斷它們是否連同的問題,一般採用兩種方法,如果只判斷連通與否,不需要給出路徑,則採用Union Find(並查集算法),如果需要給出路徑,一般採用DFS,如果採用BFS,則會造成能存消耗大,並會超時,但如果求最短路徑,則採用BFS比較好。本題只需要判斷兩點之間是否連通,所以採用並查集算法。下面對並查集算法進行簡要討論。

並查集算法的思想就是根據題目所給的點之間的關聯信息,將所有點分到不同的連通分量中,同一個連通分量中任意兩點間有連通路,上述過程稱爲Union;對比兩點所在的連通分量,如果是同一個連通分量,則他們之間連通,否則他們之間不連通。因爲在Union過程中,一個連通分量中的所有元素可能被整體加入到另一連通分量中,如何表徵不同連通分量是關鍵問題。一般將同一個連通分量的節點(任兩點間可達)用一棵樹來表示,而該樹用數組或者map來實現,比如本題用map<sting,string> directParent,來實現一棵樹,對於節點a,b,如果他們可互通,也即{a,b}在pairs中,則可選擇a,b中的任意一個的根節點作爲另一個節點根節點的父節點,逐漸構造一個森林。

find(a)用於返回節點a的祖宗節點(它所存在的數的根節點),爲了使得在union時,始終將重量小的樹連接到重量大的樹,從而避免樹的深度過大,使find複雜度加大,可以用map記錄每個根對應數的節點個數,並在union後改變其值。C++代碼如下:

class Solution {
public:
    map<string,string> directParent;//the directParent
    bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<pair<string, string>> pairs) {
        if(words1.size()!=words2.size())
            return false;
        unionString(pairs);
        int i;
        for(i=0;i<words1.size();i++){
            if(find(words1[i])!=find(words2[i]))
                return false;
        }
        return true;
    }
    void unionString(vector<pair<string,string>> pairs){
        for(auto p:pairs){
            if(find(p.first)!=find(p.second))
                directParent[find(p.first)]=find(p.second);//任意連接這兩個樹,可能造成樹的深度過大的不平衡樹
        }
    }
    string find(string a){
        return (!directParent.count(a))?a:((directParent[a]==a)?a:find(directParent[a]));//to find the root
    }
};
在find中完成了每個節點對應父節點的初始化過程,初始化爲自己。下面的代碼加入了樹節點計數功能:

class Solution {
public:
    map<string,string> directParent;//the directParent
    map<string,int> countN;
    bool areSentencesSimilarTwo(vector<string>& words1, vector<string>& words2, vector<pair<string, string>> pairs) {
        if(words1.size()!=words2.size())
            return false;
        unionString(pairs);
        int i;
        for(i=0;i<words1.size();i++){
            if(find(words1[i])!=find(words2[i]))
                return false;
        }
        return true;
    }
    void unionString(vector<pair<string,string>> pairs){
        for(auto p:pairs){
            if(find(p.first)!=find(p.second)){
                if(!countN.count(find(p.first)))
                    countN[find(p.first)]=1;
                if(!countN.count(find(p.second)))
                    countN[find(p.second)]=1;
                if(countN[find(p.first)]>=countN[find(p.second)]){
                    directParent[find(p.second)]=find(p.first);
                    countN[find(p.first)]+=countN[find(p.second)]; 
                }  
                else{
                    directParent[find(p.first)]=find(p.second); 
                    countN[find(p.second)]+=countN[find(p.first)];  
                }
                
            }    
        }
    }
    string find(string a){
        return (!directParent.count(a))?a:((directParent[a]==a)?a:find(directParent[a]));//to find the root
    }
};



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章