• 最短摘要的生成


    编程之美--3.5最短摘要生成

      Alibaba笔试题:给定一段产品的英文描述,包含M个英文字母,每个英文单词以空格分隔,无其他标点符号;再给定N个英文单词关键字,请说明思路并编程实现方法String extractSummary(String description,String[] key words),目标是找出此产品描述中包含N个关键字(每个关键词至少出现一次)的长度最短的子串,作为产品简介输出。(不限编程语言)20分。

      这道笔试题和编程之美最短摘要生成的方法类似,我用java实现了这个方法。

      先来看看这些序列:

    w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1

         问题在于,如何一次把所有的关键词都扫描到,并且不遗漏。扫描肯定是无法避免的,但是如何把两次扫描的结果联系起来呢?这是一个值得考虑的问题。

         沿用前面的扫描方法,再来看看。第一次扫描的时候,假设需要包含所有的关键词,从第一个位置w0处将扫描到w6处:

    w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1

         那么,下次扫描应该怎么办呢?先把第一个被扫描的位置挪到q0处。

    w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1

         然后把第一个被扫描的位置继续往后面移动一格,这样包含的序列中将减少了关键词q0。那么,我们便可以把第二个扫描位置往后移,这样就可以找到下一个包含所有关键词的序列。即从w4扫描到w9处,便包含了q1,q0:

    w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1

         这样,问题就和第一次扫描时碰到的情况一样了。依次扫描下去,在w中找出所有包含q的序列,并且找出其中的最小值,就可得到最终的结果。

         编程之美上给出了如下参考代码:

     

    int nTargetLen = N + 1; // 设置目标长度为总长度+1 ,是目标字符串的长度吧
    int pBegin = 0; // 初始指针 
    int pEnd = 0; // 结束指针 
    int nLen = N; // 目标数组的长度为N 
    int nAbstractBegin = 0; // 目标摘要的起始地址 
    int nAbstractEnd = 0; // 目标摘要的结束地址 
     
    while(true)

        // 假设未包含所有的关键词,并且后面的指针没有越界,往后移动指针 
     while(!isAllExisted() && pEnd < nLen)
        { 
            pEnd++; 
        } 
         
        // 假设找到一段包含所有关键词信息的字符串 
     while(isAllExisted())
        { 
            if(pEnd – pBegin < nTargetLen) //只要不是最短这个不执行,只让pbegin往后移动
            { 
                nTargetLen = pEnd – pBegin; 
                nAbstractBegin = pBegin; 
                nAbstractEnd = pEnd – 1;
            } 
            pBegin++; 
        } 
        if(pEnd >= N)
            Break; 
    }  

     

      以下是我写的java实现代码。
         使用HashMap将关键字映射到数字,减少字符串比较,能进一步提高效率。使用数组保存关键字被访问到的次数,用来判断是否包含全部关键字。

     

    package com.flyoung;

    import java.util.HashMap;
    import java.util.Map;

    public class Abstract {
    /*
     * 编程之美 最短摘要生成
     * */
       
        private int[] keywordsArray; //记录关键字被访问次数的数组
     private int pBegin=0;//查找起始点
     private int pEnd=0;//查找终点
     private int abstractBegin=0;//摘要起始点
     private int abstractEnd=0;//摘要终点
     private int targetLen;//摘要最小长度
     private Map<String,Integer> map;//将关键字映射成数字
     
        public Abstract(String[] keywords){
            int len = keywords.length;
            this.keywordsArray = new int[len];
            this.map = keywordsMap(keywords);
        }
        public String extractSummary(String description,String[] keywords){
            String[] array = description.split(" ");//将字符串转化为数组
     return extract(array,keywords);
        }
        //实际的抽取函数
     public String extract(String[] description,String[] keywords){
            String summary = "";
            int nLen = description.length;
            targetLen = nLen+1;
            while(true){
                while(!isAllExisted()&&pEnd<nLen){
                    if(this.map.get(description[pEnd])!=null){
                        setKeywordsArray(keywordsArray,this.map.get(description[pEnd]),0);
                    }
                    pEnd++;
                }
                while(isAllExisted()){
                    if(pEnd-pBegin<targetLen){
                        targetLen = pEnd-pBegin;
                        abstractBegin = pBegin;
                        abstractEnd = pEnd-1;
                    }
                    if(map.get(description[pBegin])!=null){
                        setKeywordsArray(keywordsArray,map.get(description[pBegin]),1);
                    }
                    pBegin++;
                }
                if(pEnd>=nLen){
                    break;
                }
            }
            for(int j=abstractBegin;j<=abstractEnd;j++){
                if(j!=abstractEnd){
                    summary = summary+description[j]+" ";
                }else{
                    summary +=description[j];
                }   
            }
            return summary;
        }
        public Map<String,Integer> keywordsMap(String[] keywords){
            Map<String,Integer> map = new HashMap<String,Integer>();
            int len = keywords.length;
            for(int i=0;i<len;i++){
                map.put(keywords[i], i);
            }
            return map;
        }
        //设置关键字被访问到的次数
     public void setKeywordsArray(int[] keywordsArray,int i,int flag){ //flag:0 add flag:1 sub
     if(flag==0){
                keywordsArray[i]++;
            }else{
                keywordsArray[i]--;
            }
        }
        //检查是否包含全部关键字
     public boolean isAllExisted(){
            boolean result = true;
            for(int a:keywordsArray){
                if(a==0){
                    result=false;
                    break;
                }
            }
            return result;
        }
       
        public static void main(String[] args) {
            String description="hello software hello test world spring sun flower hello";
            String[] keywords = {"hello","world"};
            Abstract nAbstract = new Abstract(keywords);
            System.out.println(nAbstract.extractSummary(description, keywords));
        }
    }

    编程之美:最短摘要生成问题...

    百度笔试:n个颜色的结点组成的圈,总个数为m,求包含n个结点最短的子链。

    #include <iostream>

    #include <string.h>

    using namespace std;

    #define CHARNUM 256

    struct pos{

            int s;

            int e;

    };

    pos abstract(char a[],int m,int n){

            pos p={0,0};

            int r[CHARNUM]={0};

            int sum(0),i(0),j(0);

            int min = m;

            while(j<m){

                    if(r[a[j]]++ == 0) sum++;

                    if(sum == n){

                            while(r[a[i]]>1 && i<j) r[a[i++]]--;

                            if(min>j-i){

                                    min = j-i;

                                    p.s = i;

                                    p.e = j;

                            }

                    }

                    j++;

            }

            return p;

    }

    int main(){

            char s[]="adcaccdddaacbbdabdbcdc";

            pos p = abstract(s,strlen(s),4);

            for(int i=0;i<strlen(s);i++)

                    cout<<s[i];

            cout<<endl;

            for(int i=0;i<strlen(s);i++)

                    if(i>=p.s && i<=p.e)

                            cout<<s[i];

                    else

                            cout<<" ";

            cout<<endl;

            cout<<"s:"<<p.s<<";e:"<<p.e<<endl;

    }

    题目:抽象点说,就是在一个字符串中,找一些目标字符串,找到包含所有目标字符串的最小字符串。题目虽然叫做最短摘要生成,但和实际的搜索snippet的计算还是有比较大的差距的。

    解法:文献[1]给出了一种比较好的解法,策略还是使用双指针,双指针对于很多算法设计很有价值,算法的思想是采用两个指针,开始两个指针都指向缓冲区的头部,尾指针向后扫描,直到头指针和尾指针中间包含了全部的关键字,那么头指针向后移动,直到包含全部关键字这个条件失败,这时截取字串并和已取得的最小字串比较,如果小则替换。头指针、尾指针都向后一个位置(这点很重要,开始就忘记了移动头指针,导致程序出错),继续扫描。另外,由于一个关键字可能重复多次,因此在判断是否包含全部关键字时要采用计数机制,才能保证查看的准确。这样只要头指针和尾指针扫描两次字符串就可以完成生成算法。

    具体代码如下:

    [cpp] view plaincopy

    1. #include <stdio.h>  
    2. #include <string>  
    3. #include <map>  
    4. class KeyWordChecker {  
    5.  public:  
    6.   KeyWordChecker() : current_state_(false) {}  
    7.   void AddKeyWord(const std::string& word) {  
    8.     if (keywords_.find(word) == keywords_.end()) {        
    9.       keywords_[word] = 0;  
    10.     }  
    11.   }  
    12.   bool IsContainAllKeyWord(const std::string& word, bool add) {  
    13.     if (keywords_.find(word) == keywords_.end()) {  
    14.       return current_state_;  
    15.     }  
    16.     if (add) {  
    17.       keywords_[word]++;  
    18.     } else {  
    19.       keywords_[word]--;  
    20.     }  
    21.     std::map<std::string, int>::iterator begin = keywords_.begin();  
    22.     int counter = 0;  
    23.     while (begin != keywords_.end()) {  
    24.       if (begin->second > 0) {  
    25.         counter++;  
    26.       } else {  
    27.         break;  
    28.       }  
    29.       begin++;  
    30.     }  
    31.     if (counter == keywords_.size()) {  
    32.       current_state_ = true;  
    33.       return true;  
    34.     } else {  
    35.       current_state_ = false;  
    36.       return false;  
    37.     }  
    38.   }  
    39.  private:  
    40.   std::map<std::string, int> keywords_;  
    41.   bool current_state_;  
    42. };  
    43.   
    44. std::string GenerateSnippet(const std::string& content, KeyWordChecker* keyword_checker) {  
    45.   int begin = 0;  
    46.   int end = 0;  
    47.   std::string snippet;  
    48.   int min_length = content.size();  
    49.   while (end < content.size()) {  
    50.     if (!keyword_checker->IsContainAllKeyWord(std::string(1, content[end]), true)) {  
    51.        end++;  
    52.        continue;  
    53.     }  
    54.     while (begin <= end && keyword_checker->IsContainAllKeyWord(std::string(1, content[begin]), false)) {  
    55.       begin++;  
    56.     }  
    57.     if (end - begin + 1 < min_length) {  
    58.       snippet = content.substr(begin, end - begin + 1);  
    59.       min_length = end - begin + 1;  
    60.     }  
    61.     end++;  
    62.     begin++;  
    63.   }    
    64.   return snippet;  
    65. }  
    66. int main(int argc, char** argv) {  
    67.   std::string content = "abbbbbcaaadebmmmmdcfg";  
    68.   KeyWordChecker keyword_checker;  
    69.   keyword_checker.AddKeyWord("b");  
    70.   keyword_checker.AddKeyWord("d");  
    71.   std::string snippet = GenerateSnippet(content, &keyword_checker);  
    72.   printf("snippet:%s ", snippet.c_str());  
    73. }  
  • 相关阅读:
    Oracle如何查询不等于某数值
    《Linux系列》- 查看Linux日志
    《数据库优化》- MySQL视图
    《数据库优化》- MySQL优化
    《面试经典系列》- MySQL数据库存储引擎
    《面试经典系列》- Java获取反射机制的三种方法
    《面试经典系列》- 从底层理解==和equals的区别
    《面试经典系列》- 乐观锁和悲观锁及其应用场景
    数据结构之HashMap
    收藏学习地址
  • 原文地址:https://www.cnblogs.com/fickleness/p/3155041.html
Copyright © 2020-2023  润新知