尺取法之最短摘要的问题

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;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值