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}