给出两个单词,找到它们的最短距离 (以它们之间隔了多少个单词计数)。

原创
2016/11/28 14:22
阅读数 2.7K
  • 有一篇文章内含多个单词,现给定两个单词,请设计一个高效算法,找出文中这两个单词的最短距离(即最少相隔的单词数,也就是两个单词在文章中位置的差的绝对值)。
  • 给定一个string数组article,代表所给文章,同时给定文章的单词数n和待查找的两个单词x和y。请返回两个单词的最短距离。保证两个单词均在文中出现且不相同,同时保证文章单词数小于等于1000。
  • 代码如下
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)解法

如果要找多次就多用一个Hashtable,把所有的组合都保存起来

 

 

 

[java] view plain copy

在CODE上查看代码片派生到我的代码片

  1. package Hard;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.HashSet;  
  5. import java.util.Map;  
  6.   
  7. import CtCILibrary.AssortedMethods;  
  8.   
  9.   
  10. /** 
  11.  * You have a large text file containing words. Given any two words, find the shortest distance (in terms of number of words) between them in the file. Can you make the searching operation in O(1) time? What about the space complexity for your solution? 
  12.  
  13. 译文: 
  14.  
  15. 有一个很大的文本文件,里面包含许多英文单词。给出两个单词,找到它们的最短距离 (以它们之间隔了多少个单词计数)。你能在O(1)的时间内返回任意两个单词间的最短距离吗? 你的解法空间复杂度是多少? 
  16.  * 
  17.  */  
  18. public class S18_5 {  
  19.   
  20.     // O(n)  
  21.     public static int shortest(String[] words, String word1, String word2) {  
  22.         int min = Integer.MAX_VALUE;  
  23.         int lastPosWord1 = -1;  
  24.         int lastPosWord2 = -1;  
  25.         for (int i = 0; i < words.length; i++) {  
  26.             String currentWord = words[i];  
  27.             if (currentWord.equals(word1)) {  
  28.                 lastPosWord1 = i;  
  29.                 // Comment following 3 lines if word order matters  
  30.                 int distance = lastPosWord1 - lastPosWord2;  
  31.                 if (lastPosWord2 >= 0 && min > distance) {  
  32.                     min = distance;  
  33.                 }  
  34.             } else if (currentWord.equals(word2)) {  
  35.                 lastPosWord2 = i;  
  36.                 int distance = lastPosWord2 - lastPosWord1;  
  37.                 if (lastPosWord1 >= 0 && min > distance) {  
  38.                     min = distance;  
  39.                 }  
  40.             }  
  41.         }  
  42.         return min;  
  43.     }  
  44.   
  45.     //===============================================================================  
  46.     private static Map<HashSet<String>, Integer> distances =   
  47.             new HashMap<HashSet<String>, Integer>();  
  48.        
  49.     public static int query(String word1, String word2) {  
  50.           
  51.         HashSet<String> pair = new HashSet<String>();  
  52.         pair.add(word1);  
  53.         pair.add(word2);  
  54.         if(distances != null && distances.containsKey(pair)){  
  55.             return distances.get(pair);  
  56.         }  
  57.         return Integer.MAX_VALUE;  
  58.           
  59.     }  
  60.       
  61.     public static void buildMap(String[] wordlist) {  
  62.         // build the mapping between pairs of words to  
  63.         // their shortest distances  
  64.         for (int i = 0; i < wordlist.length; ++i) {  
  65.             for (int j = i + 1; j < wordlist.length; ++j) {  
  66.                 if (!wordlist[i].equals(wordlist[j])) {  
  67.                     HashSet<String> pair = new HashSet<String>();  
  68.                     pair.add(wordlist[i]);  
  69.                     pair.add(wordlist[j]);  
  70.                     if (distances.keySet().contains(pair)) {  
  71.                         int curr = distances.get(pair);  
  72.                         if (j - i < curr)  
  73.                             distances.put(pair, j - i);  
  74.                     } else {  
  75.                         distances.put(pair, j - i);  
  76.                     }  
  77.                 }  
  78.             }  
  79.         }  
  80.     }  
  81.       
  82.       
  83.       
  84.     public static void main(String[] args) {  
  85.         String[] wordlist = AssortedMethods.getLongTextBlobAsStringList();  
  86.         System.out.println(AssortedMethods.stringArrayToString(wordlist));  
  87.   
  88.         String[][] pairs = { { "Lara", "the" }, { "river", "life" },  
  89.                                   { "path", "their" }, { "life", "a" } };  
  90.           
  91.         buildMap(wordlist);  
  92.           
  93.         for (String[] pair : pairs) {  
  94.             String word1 = pair[0];  
  95.             String word2 = pair[1];  
  96.             int distance = shortest(wordlist, word1, word2);  
  97.             System.out.println("Distance between <" + word1 + "> and <" + word2 + ">: " + distance + ", " + query(word1, word2));  
  98.         }  
  99.     }  

 

题目

有一个很大的文本文件,里面包含许多英文单词。给出两个单词,找到它们的最短距离 (以它们之间隔了多少个单词计数)。你能在O(1)的时间内返回任意两个单词间的最短距离吗? 你的解法空间复杂度是多少?

解答

先看一个例子,为了简单起见,我们假设文件里就只有以下两句话。然后, 我们现在来求is和name的最短距离。假设相邻的两个单词距离为1。

 

1

2

What is your name My name is Hawstein

 

 

首先,我们遇到的第一个问题是:是否要考虑顺序?我们求的是is和name间的距离, 那么文本中先出现name再出现is的情况要不要算进来。这一点是要和面试官进行交流确认的。 这里我们假设不考虑顺序,并且认为本文中只有单词,没有标点。 为了进一步简化问题,我们可以用一个字符串数组来保存单词, 接下来考虑如何计算两个单词间的最短距离。

最直观的一个解法是,遍历单词数组,遇到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}

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
在线直播报名
返回顶部
顶部