合并整理最短摘要生成算法

来源:http://www.cnblogs.com/pangxiaodong/archive/2011/09/08/2171551.html

http://www.cnblogs.com/jack204/archive/2012/09/03/2668667.html

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

题目:

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

先来看看这些序列:

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

分析

当初看这道题时,看了好了几遍都没看懂。后来总算弄明白:给出的字符串是用其它程序分好词的,关键字符串也是用其它程序分好词的,而不是按用户直接输入的字符串。比如书上给的例子:“微软亚洲研究院 使命”,不是按空格分成两个关键词,“微软亚洲研究院”和“使命”,而是按其它程序分成:“微软”、“亚洲”、“研究院”和“使命”四个关键词。

“最短摘要”应该是指:包含所有关键字(关键字不要求按用户输入的顺序排列)的长度最短的摘要。书上的解法,把“最短摘要”理解成包含所有关键字且词个数最少的摘要。

 

这个过程要记录最短文摘的信息。
这个时间复杂度是 O(N ^ 2 * M)
N 是文档的长度
M 是关键词数组的大小

总结的滑动窗口法是:

------------------------------------------------------

使用滑动窗口的办法,找出最短摘要。我们把这个滑动窗口叫做摘要滑动窗口
摘要滑动窗口左边界L,右边界R。

窗口中应该维护的信息:

  1.   窗口中已经遍历过的关键字序列----可使用队列才存储;
  2.   窗口中各个关键字出现的个数----可使用hashtable来表示或者数组也行。

[while]

右边界R向右移动的原则:
  当前窗口中不包含所有种类的关键字,R向右移动寻找更新的关键字。

左边界L向右移动的原则:
  当前窗口中已经包含了所有种类的关键字,计算当前摘要长度,并从队列中拿出一个关键字,即L向右移动一个关键字;

L与R一直移动下去,一直到R不能往右移动时候,循环结束。

[end while]


代码实现:

1. 简述

    这道题的题干说的不是特别清楚,在网上看了几篇相关的博文才搞清楚,对于算法本身就是编程之美给出的解法,现在我还没有深入的理解。题干大意如下:输入两个字符串,一个表示用户输入的查询,另一个表示一篇文档的内容。对于查询和文档分别进行自动分词后,用户查询和文档内容的两个字符串变为两个词语序列。比如,下面的keyword表示分词后的用户查询,str表示分词后的文档内容。

复制代码
   string  keyword[]  =  {  " 微软 " " 计算机 " " 亚洲 " " 中国 " };
  
string  str[]  =  { 
    
" 微软 " , " 亚洲 " , " 研究院 " , " 成立 " , " " , " 1998 " , " " , " " , " 我们 " , " " , " 使命 " ,
    
" " , " 使 " , " 未来 " , " " , " 计算机 " , " 能够 " , " " , " " , " " , " " , " " , " " ,
    
" " , " " , " 自然语言 " , " " , " 人类 " , " 进行 " , " 交流 " , " " , " " , " " , " 基础 " , " " ,
    
" " , " 微软 " , " 亚洲 " , " 研究院 " , " " , " " , " 促进 " , " 计算机 " , " " , " 亚太 " , " 地区 " ,
    
" " , " 普及 " , " " , " 改善 " , " 亚太 " , " 用户 " , " " , " 计算 " , " 体验 " , " " , " "
  };
复制代码

    所要求得是str1中的若干个连续的字符串,假设为str[i]-str[j],其中包含keyword中的所有词语,且str[i]-str[j]是满足这个条件的最短的一个。

2. 思路

    这个主要注意几点:首先,str[i]-str[j]包含keyword里面的所有词语,但是不要求顺序相同,然后,str[i]-str[j]是所有满足这样要求的最短子串。
    主要方法是:deque<index> store: 记录当前摘要的所有单词在str中的下标,map<string, int> record记录所有当前摘要中出现的次数。min_len:当前最短摘要的长度。min_index_first:当前最短摘要的第一个词语在str中的下标,min_index_last:当前最短摘要的最后一个词语在str中的下标。    
    第一步,寻找第一个完整摘要,更新store和record。如果没找到,程序结束。如果找到了计算min_len,min_index_first,min_index_last;,然后进行第二步。
    第二步,队列中去掉一个第一个关键词,同时更新record,如果record中该关键词计数还大于0,这说明虽然当前摘要短了,但是还是完整的,因此这必然是一个更短的摘要,min_len--;min_index_first++; 重复第二步。 如果record中关键词的计数为0了,这说明当前摘要不完整了,需要向后面找缺少的关键词,进入第三步。
   第三步,在当前摘要后面寻找缺少的关键词。如果下标越界了,说明不能再找到完整的摘要了,停止工作即可。如果找到的不是关键词,index++,即向后移动继续找。如果找到的是关键词更新store和record,此时如果找到的关键词刚好还是缺少的关键词,那么转向第二步,无论与否,记得index++先。

3. 代码实现

   大体思路不难,但是把当前摘要的关键词下标序列,当前摘要的关键词计数,最短摘要位置,缺少的关键词,这些数据放在一起,还要更新,逻辑就有点复杂了。写了好一会才写出来,不好说对与不对,毕竟没有完整的测试。  

复制代码
#include < iostream >
#include
< string >
#include
< map >
#include
< deque >
using   namespace  std;

void  find_min_len_abstract( string  str[],  string  keyword[],  int  len_str,  int  len_keyword) {
//  初始化map 
  map < string , int >  record;
  
for ( int  i = 0 ; i < len_keyword; i ++ ) {
    record[keyword[i]] 
=   0 ;
  }
  
//  匹配过程
  deque < int >  store;  //  存储的是str中关键词的下标 
   int  min_len  =   0 ;
  
int  min_index_first  =   - 1 ;
  
int  min_index_last  =   - 1 ;
  
int  find_key_num  =   0 ;
  
int  index  =   0 ;
  
while (find_key_num  <  len_keyword  &&  index  <  len_str) {
    
if (record.find(str[index])  ==  record.end()) {  //  str[index]不是关键字 
       index ++ ;
    }
    
else  {  //  str[index]是关键字 
       if (record[str[index]]  ==   0 //  第一次找到这个关键字
        find_key_num  ++ ;
      record[str[index]] 
=  record[str[index]]  +   1 //  计数加1
      store.push_back(index); 
      index
++ ;
    }
  }
  
if (find_key_num  <  len_keyword) {  //  一个满足的摘要都没找到 
    cout  <<   " not abstract found  "   <<  endl;
  }
  
else  {  //  找到一个了,试着找找更好的 
    min_len  =  store.back() -  store.front()  +   1 ;   
    min_index_first 
=  store.front();
    min_index_last 
=  store.back();
    
//  第一个摘要 
    cout  <<   " 第一个摘要 "   <<  endl; 
    cout 
<<   " min len:  "   <<  min_len  <<  endl;
    
for ( int  i = min_index_first; i <= min_index_last; i ++ )
      cout 
<<  str[i]  <<   "   " ;
    cout 
<<  endl; 
    cout 
<<   " --------------------------------------------- "   <<  endl;
    
string  need_key;
    
bool  already_found  =   true ;
    
while ( true ) {   
      
if (already_found  ==   true ) {  //  刚好找到一个新摘要 
         string  first_key  =  str[store.front()];
        record[first_key]
-- //  减少当前最前面的关键词 
        store.pop_front();  //  关键词出队 
         if (record[first_key]  ==   0 ) {  //  如果该关键词没了 
          already_found  =   false ;
          need_key 
=  first_key;  //  记录需要寻找的关键词 
        }               
        
else  {  //  少了词语,但是还包含所有关键词,说明这是一个更短的摘要 
          min_len -- ;
          min_index_first 
++ ;
          cout 
<<   " 更短的摘要 "   <<  endl;
          cout 
<<   " min len:  "   <<  min_len  <<  endl;
          
for ( int  i = min_index_first; i <= min_index_last; i ++ )
            cout 
<<  str[i]  <<   "   " ;
          cout 
<<  endl; 
          cout 
<<   " --------------------------------------------- "   <<  endl;
        }
      }
      
else  {  //  需要向后面找满足条件的关键词 
         if (index  >=  len_str) {  //  不可能找到需要的关键词了 
           break ;
        }
        
else   if (record.find(str[index])  ==  record.end()) {  //  不是关键词 
          index ++ ;
        }
        
else  {  //  是关键词 
          record[str[index]]  =  record[str[index]] + 1 ;
          store.push_back(index); 
          
if (str[index]  ==  need_key) {  //  正好还是需要找到的关键词
             already_found  =   true ;
             
if ((store.back() - store.front()  +   1 <  min_len) {  //  新的摘要更短 
               min_len  =  store.back()  -  store.front()  +   1 ;
               min_index_first 
=  store.front();
               min_index_last 
=  store.back();
               
//  更短的摘要
               cout  <<   " 更短的摘要 "   <<  endl;
               cout 
<<   " min len:  "   <<  min_len  <<  endl;
               
for ( int  i = min_index_first; i <= min_index_last; i ++ )
                 cout 
<<  str[i]  <<   "   " ;
               cout 
<<  endl; 
               cout 
<<   " --------------------------------------------- "   <<  endl;
             }
             
else  {
               cout 
<<   " 并非更短的摘要 "   <<  endl;
               cout 
<<   " min len:  "   <<  store.back()  -  store.front()  +   1   <<  endl;
               
for ( int  i = store.front(); i <= store.back(); i ++ )
                 cout 
<<  str[i]  <<   "   " ;
               cout 
<<  endl; 
               cout 
<<   " --------------------------------------------- "   <<  endl;
             }
          }
          index
++
        } 
//  else
      }  //  else
    }  //  while
  }  //  else
  
//  输出结果
}

int  main() {  
  
//  string keyword[] = { "微软", "计算机", "亚洲", "中国"};
   string  keyword[]  =  {  " 微软 " " 计算机 " " 亚洲 " };
  
string  str[]  =  { 
    
" 微软 " , " 亚洲 " , " 研究院 " , " 成立 " , " " , " 1998 " , " " , " " , " 我们 " , " " , " 使命 " ,
    
" " , " 使 " , " 未来 " , " " , " 计算机 " , " 能够 " , " " , " " , " " , " " , " " , " " ,
    
" " , " " , " 自然语言 " , " " , " 人类 " , " 进行 " , " 交流 " , " " , " " , " " , " 基础 " , " " ,
    
" " , " 微软 " , " 亚洲 " , " 研究院 " , " " , " " , " 促进 " , " 计算机 " , " " , " 亚太 " , " 地区 " ,
    
" " , " 普及 " , " " , " 改善 " , " 亚太 " , " 用户 " , " " , " 计算 " , " 体验 " , " " , " "
  };
  
int  len_keyword  =   sizeof (keyword) / sizeof ( string );
  
int  len_str  =   sizeof (str) / sizeof ( string );
  find_min_len_abstract(str, keyword, len_str, len_keyword);
  
  system(
" PAUSE " );
  
return   0 ;
}
复制代码

    输出结果如下:

 

4. 参考

    最短摘要的生成    http://www.cnblogs.com/flyinghearts/archive/2011/03/24/1994453.html



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值