有一字符串由M个单词组成单词之间有空格隔开(只有空格,没有其他标点符号),有N个关键字,现在要在字符串中找出包含N个关键字(每个关键字至少出现一次,没有说要不要按什么顺序)的最短子串。函数原型:String extractSummary(String description, String[] keywords)
-----------------------------------------------------------------------------------
1.为了方便处理。先将单词转换成数字。
转换方式: 将所以出现的单词按字符串顺序排序, 之后每一个字符串都可以用二分查找找到一个下标,该下标即为该字符串转换之后的数字。于是问题转换为,给定一个数组和一个集合。 找出该数组中一个最短的连续子序列,使得该子序列包含集合中的所有数字。
2. 针对数字的处理方法:
设要查找的列表为数组 A 。 令设一数组B, 数组B[i]=(A[i]的下一个在A数组中的出现位置,倘若后面没有出现则记为-1)。
设两个指针 i, j均指向 A数组最开始的位置。 让J逐步右移,直到前j个元素满足包含集合所以元素的条件为止,此时便得到第一个合法解: [I,J]。
指针i开始扫描,直到 b[i] > j 或者b[j] == -1。 当b[i] > j时,说明下一个a[i]已经不再序列区间[i,j]范围内。 此时令j = b[i],然后继续扫描。 直到扫描结束, 可以保证任意时刻序列区间[i,j]都是合法的。 记录一个全局最优解就好了 。
得到数字结果以后,再根据结果转换为原字符串的子串就好了。
------------------------------------------------------------------------------------
先得到所有关键字的位置,之后求包含所有关键字的最短长度是个很经典的问题。
可以有nlog(k)的解法,n是所有位置的个数,k是关键字的个数
解法如下:
对于每个关键字,它所有的位置排序放置。
同时对每个关键字设置一个当前哨卡位置,初值都指向第0个元素,计算最短长度。
从中找出一个起始位置最小的关键字元素,其位置向后移动一个(因为只有移动它,最短长度才有可能减少),再得到当前的最短长度
重复上述过程,直到某个关键字无法继续后移。
下面的代码简化了一点,只考虑了起始位置。
vector< vector<int> > allPos; //to each keyword, the positions are sorted int keyNum; int getMin() { vector<int> guard(keyNum); for (int i = 0; i < keyNum; i++) guard[i] = 0; int ret = INT_MAX; priority_queue< pair<int, int>, vector< pair<int, int> >, greater< pair<int, int> > > queue1; pair<int, int> maxp; maxp.first = -1; for (int i = 0; i < keyNum; i++) { pair<int, int> tmp = make_pair(allPos[i][0], i); queue1.push(tmp); maxp = max(maxp, tmp); } do { pair<int, int> p1 = queue1.top(); ret = min(ret, maxp.first - p1.first + 1); int id = p1.second; queue1.pop(); guard[id]++; if (guard[id] >= (int)allPos[id].size()) break; pair<int, int> tmp = make_pair(allPos[id][guard[id]], id); queue1.push(tmp); maxp = max(maxp, tmp); } while (1); return ret; }