public class Distance { public int getDistance(String[] s, int n, String x, String y) { //兩個單詞均在文中出現且不相同 int startX=-1, endY = -1;//1和0之間間隔爲1 int minD = Integer.MAX_VALUE; for(int i=0; i<s.length; i++){ if(s[i].equals(x)) startX=i; else if(s[i].equals(y)) endY=i; else continue; //兩個元素的距離:startX-endY的絕對值,與當前記錄的最小間距minD比,取小的 if(startX!=-1 && endY!=-1) minD=Math.min(Math.abs(startX-endY), minD); } return minD; } }
若是隻要找一次就用第一種O(n)解法java
若是要找屢次就多用一個Hashtable,把全部的組合都保存起來面試
[java] view plain copy算法
有一個很大的文本文件,裏面包含許多英文單詞。給出兩個單詞,找到它們的最短距離 (以它們之間隔了多少個單詞計數)。你能在O(1)的時間內返回任意兩個單詞間的最短距離嗎? 你的解法空間複雜度是多少?數組
先看一個例子,爲了簡單起見,咱們假設文件裏就只有如下兩句話。而後, 咱們如今來求is和name的最短距離。假設相鄰的兩個單詞距離爲1。app
1函數 2ui |
What is your name My name is Hawsteinspa
|
首先,咱們遇到的第一個問題是:是否要考慮順序?咱們求的是is和name間的距離, 那麼文本中先出現name再出現is的狀況要不要算進來。這一點是要和麪試官進行交流確認的。 這裏咱們假設不考慮順序,而且認爲本文中只有單詞,沒有標點。 爲了進一步簡化問題,咱們能夠用一個字符串數組來保存單詞, 接下來考慮如何計算兩個單詞間的最短距離。.net
最直觀的一個解法是,遍歷單詞數組,遇到is或name就更新它們的位置, 而後計算is和name之間的距離,若是這個距離小於以前的最小距離,則更新這個最小距離。 看圖示:設計
1 2 3 4 |
What is your name My name is Hawstein 0 1 2 3 4 5 6 7 p
|
p表示遍歷的當前位置。此時已經通過了前面的一個is和name,is位置爲1,name位置爲3, 最小距離min=3-1=2。當p移動到下一個單詞,發現是name,則更新name的位置爲5, 減去is的位置1獲得4,並不小於min,不更新,繼續。當p移動到is,更新is的位置爲6, 減去name的位置5,獲得距離爲1,小於min,更新min=1。p以後一直移動到末尾, 沒遇到is或name,再也不更新。最後返回最小值min。時間複雜度O(n),空間複雜度O(1)。
代碼以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
int ShortestDist(string text[], int n, string word1, string word2){ int min = kMaxInt / 2; int pos1 = -min; int pos2 = -min;
for(int pos=0; pos<n; ++pos){ if(text[pos] == word1){ pos1 = pos; int dist = pos1 - pos2; if(dist < min) min = dist; } else if(text[pos] == word2){ pos2 = pos; int dist = pos2 - pos1; if(dist < min) min = dist; } }
return min; } |
題目要求在O(1)的時間內返回兩個單詞的最短距離,上述代碼確定是沒法知足要求的。 那要怎麼作呢?只能用哈希表作預處理了,空間換時間。
方法一
遍歷一次文本,用哈希函數將每一個單詞映射到不一樣結點,結點後保存該單詞出現的位置。 好比對於上面的例子
1 2 3 |
What is your name My name is Hawstein 0 1 2 3 4 5 6 7
|
遍歷一次並處理後,咱們獲得每一個單詞在文本中出現的位置:(哈希值是隨便寫的,示意用)
1 2 3 4 5 6 7 8 |
單詞 哈希值 出現位置 What: 3 0 is: 7 1, 6 your: 13 2 name: 14 3, 5 My: 25 4 Hawstein: 27 7
|
求兩個單詞間的最小距離時,首先用O(1)時間經過哈希函數映射到指定結點, 而後對於其中一個單詞的每一個位置,去與第二個單詞的全部位置比較,找到最小的差值。 因爲位置是遞增的,所以能夠修改二分查找進行搜索。
該方法的平均查找複雜度應該是O(1)的,但最壞狀況下沒法保證O(1)的查找時間, 考慮一種極端狀況,文本中的單詞就只有is和name,它們的數量各爲(½)n, 使用這種算法,咱們須要O(nlogn)的時間。
方法二
預處理階段把文本中任意兩個單詞間的最小距離計算出來, key是兩個單詞鏈接後的哈希值,value保存的就是最小距離。 查找階段就只須要把兩個單詞鏈接求其哈希值,而後直接返回其對應的value便可。 查找兩個單詞的最小距離時間複雜度O(1)。須要O(n2 )的時間來作預處理。
因爲咱們是不考慮順序的,所以作兩個單詞的鏈接時,不能直接鏈接, 這樣會致使is和name鏈接後是isname,而name和is鏈接後nameis, 它們的哈希值不同,這並非咱們想要的。所以,在作兩個單詞的鏈接時, 咱們可讓第一個字符較小的單詞放在前面(反正定義一個規則來保證鏈接的惟一性便可)。 好比對於name和is,因爲在字典序中,i<n,因此鏈接是isname。
仍是用上面的例子,預處理後獲得:(哈希值是隨便寫的數字,示意用)
1 2 3 4 5 6 7 8 |
單詞鏈接 哈希值 最小距離 (isWhat) 8 1 ... ... ... (isname) 12 1 ... ... ... (isMy) 33 2 ... ... ...
|
這樣當我要求is和name之間的最小距離時,就只須要先鏈接它們獲得isname, 而後用哈希函數求出isname的哈希值12,而後直接返回它對應的最小距離便可。
若是有衝突怎麼辦?即兩個不一樣的字符串映射到同一個哈希值,咱們能夠用鏈地址法, 把衝突的鏈接字符串連接起來,這樣每一個結點就須要保存鏈接字符及其對應的最小距離。 好比對於上面的例子,假設isname和isMy的哈希值相同,咱們能夠按以下所示去作:
1 2 3 4 5 6 |
哈希值 最小距離 8 (isWhat,1) ... ... 12 (isname,1) -> (isMy,2) ... ...
|
這樣一來,當咱們求得一個鏈接字符串str的哈希值是12, 就依次去與其後面的結點作比較。若是str等於isname,返回1;不然,移動到下一個結點, 繼續比較。若是str等於isMy,返回2。
方法三
也能夠先將兩個單詞分別映射到兩個哈希值,好比is映射到哈希值i,name映射到哈希值j, 而後將它們的最小距離保存在d[i][j]中。這裏因爲是不考慮單詞順序的,所以, 咱們能夠將較小的哈希值放在d的第一維,較大的放在第二維。也就是對於d[i][j], 有i<j。一樣,這種方法也要考慮衝突問題。
解法1:咱們假設單詞word1和word2誰在前誰在後可有可無。要解決此題,咱們須要遍歷一次這個文件。在遍歷期間,咱們會記下最後看見word1和word2的地方,並把它們的位置存入lastPosWord1和lastPosWord2中。碰到word1時,就拿他跟lastPosWord2比較,若有必要則更新min,而後更新lastPosWord1.每碰到word2時,咱們也執行一樣的操做。遍歷結束後,就能夠獲得最短距離。
實現算法:
int ShortestDist(string text[], int n, string word1, string word2){ int min = kMaxInt / 2; int pos1 = -min; int pos2 = -min; for(int pos=0; pos<n; ++pos){ if(text[pos] == word1){ pos1 = pos; int dist = pos1 - pos2; if(dist < min) min = dist; } else if(text[pos] == word2){ pos2 = pos; int dist = pos2 - pos1; if(dist < min) min = dist; } } return min; }
若是上述代碼要重複調用(查詢其餘單詞對的最短距離),能夠構造一個散列表,記錄每一個單詞及其出現的位置。而後,咱們只需找到listA和listB中(算術)差值最小的那兩個值。
hash_map<string,vector<int> > listA;
hash_map<string,vector<int> > listB;
listA:{1,2,9,15,25}
listB:{4,10,19}