1. 问题描述:
给定一段产品的英文描述,包含M个单词,每个英文单词以空格分隔,无其他的标点符号
再给定N个英文单词关键字,要找出此产品描述中的包含的N个关键字(每个关键字至少出现一次)的长度最短的子串,作为产品简介输出,请说明思路并变成实现方法
2. 假设存储单词的变量为words,存储要查找的关键字为keys
这道题目可以使用尺取法来求解,尺取法相当于一把尺子,在最外层的for循环中,一开始可以使用二分法来寻找当前下标所存储的单词是否为关键字,如果是关键字那么二分查找的方法将返回大于零的数字,那么循环继续往下执行,如果不是继续continue查找直到找到第一个关键字,那么尺子的开头就固定好了,接下来,使用一个for循环来寻找其余的关键字,当当前下标不是关键字的时候continue继续下一个知道找到囊括了所有关键字的下标j,找到之后判断与原来定义的len长度,若小于则更新len,而且更新囊括所有关键字的开头和结尾
需要注意的是尺子中的i至j之间可能含有更短的摘要,所以在最外层的循环中还需要加上判断条件来查找是否有更短的
需要注意的问题可能会出现下标越界的问题(到最后传递变量j的时候发现没有找到j可能等于words的长度)
3. 具体的代码实现如下:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Main{
public static void main(String[] args) {
//当关键字有重复的时候把关键字放入到map中可以很方便的处理关键字重复带来的问题
solve(new String[]{"a","ab","a","a","b","c","d","h","e","f","f","c","c","d","e","f","d","h","q"},
new String[]{"a","e"});
}
private static void solve(String[] words, String[] keys) {
int len = Integer.MAX_VALUE;
int begin = 0;
int end = -1;
int j = 0;
for(int i = 0;i<words.length;i++){
//使用二分查找来查找数组中是否含有某个关键字的下标,假如当前下标所在的单词不是关键字直接返回-1
int index = Arrays.binarySearch(words, words[i]);
if(index==-1){
continue;
}
//i可能找到一个关键字但是最后没有找到符合的所以j到最后可能到达了words的长度所以需要加上j<words.length这个条件才不会越界
if(j-i+1>=keys.length&&j<words.length&&containAll(words, keys, i, j)){
if(j-i+1<len){
len = j-i+1;
begin = i;
end = j;
}
//继续寻找,因为有可能上一个串中嵌套着更小的串
continue;
}
if(j==0){
j = i + 1;
}
//尺取法搜寻囊括关键字的的下标
for(;j<words.length;j++){
index = Arrays.binarySearch(words, words[j]);
if(index==-1){
continue;
}else{
if(containAll(words,keys,i,j)){
if(j-i+1<len){
len = j-i+1;
begin = i;
end = j;
}
break;
}
}
}
}
print(words,begin,end);
}
private static void print(String[] words, int begin, int end) {
System.out.println(begin+" "+end);
for(int i = begin;i<=end;i++){
System.out.print(words[i]+" ");
}
}
private static boolean containAll(String[] words, String[] keys, int i, int j) {
//把单词放入map中可以很好的处理关键字重复的问题
Map<String,Integer> map1 = new HashMap<String,Integer>();
for(int k = 0;k<keys.length;k++){
String key =keys[k];
if(map1.get(key)==null){
map1.put(key, 1);
}else{
//关键字重复
map1.put(key,map1.get(key)+1);
}
}
Map<String,Integer> map2 = new HashMap<String,Integer>();
for(int k = i;k<=j;k++){
String key = words[k];
if(map2.get(key)==null){
map2.put(key, 1);
}else{
//例如关键字b有两个那么当map.getKey("bb")==2
map2.put(key,map2.get(key)+1);
}
}
//遍历关键字判断i到j中的所有单词是否囊括在内
for(Map.Entry<String, Integer> e:map1.entrySet()){
if(map2.get(e.getKey())==null||map2.get(e.getKey())<e.getValue()){
return false;
}
}
return true;
}
}