《编程之美》读书笔记24: 3.5 最短摘要的生成

《编程之美》读书笔记24:  3.5 最短摘要的生成


 

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

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

   

弄清了问题,解决起来就很简单:

   1 反复读入字符串,直到碰到关键字(可以用setunordered_set)。

   2 更新该关键字字符串最近出现的位置。

   3 若已经找到所有的关键字,则根据这些关键字的位置最小/最大值,计算摘要长度。

      可以用set来维护这些位置值。

      (实际上,只要求维护位置的最小值,还可以自行实现一个堆结构,节省空间。)

根据位置值计算长度,需要先计算出分词后的字符串,在未分词的字符串的位置。

   4 记录长度最短的摘要

 

若有m个关键字,待查询字符串有n个,时间复杂度大概为:O(n*log m)

(关键字一般都很短,可以认为对关键字间的比较、哈希时间复杂度为O(1)

 另外,将关键字映射到数字,减少字符串比较,能进一步提高效率。

 

 

最短摘要
#include < iostream >
#include
< vector >
#include
< string >
#include
< set >

#define  USE_CPLUSPLUS0x 1

#if  USE_CPLUSPLUS0x
  #include
< unordered_map >
#else
  #include
< map >
#endif

#include
< cassert >

using  std:: string ;
using  std::vector;
using  std::cout;

/*       //----
#if USE_CPLUSPLUS0x
  struct Cmp{
    bool operator()(const string* a, const string* b) const { return *a == *b; }
  } ;
  
  struct Hash{
    size_t operator()(const string* a) const { return std::hash<string>()(*a);}
  } ; 
  
  typedef std::unordered_map<const string*, size_t, Hash, Cmp> Dict;
#else
  struct Cmp{
    bool operator()(const string* sa, const string * sb) const { return *sa < *sb; }
  } ;

  typedef std::map<const string*, size_t, Cmp> Dict;
#endif 
 
*/
 
#if  USE_CPLUSPLUS0x
  typedef std::unordered_map
< string , size_t >  Dict;
#else
  typedef std::map
< string , size_t >  Dict;
#endif


template
< typename T >
void  output(T beg, T end,  const   char  str[]  =   "" const   char  sep[]  =   "" )
{
  cout 
<<  str;
  
while  (beg  !=  end) cout  <<   * beg ++   <<  sep;
  cout 
<<   " \n " ;
}

void  solve( const   string  str[],  const  size_t str_sz, 
           
const   string  keyword[],  const  size_t keyword_sz)
{
  
if  (str_sz  ==   0   ||  keyword_sz  ==   0 return ;
  
  Dict key_idx;          
// 关键字映射为数字,以减少字符串比较            
   for  (size_t i  =   0 ; i  <  keyword_sz;  ++ i) {
    
// key_idx.insert(Dict::value_type(&keyword[i], i));  // ----
    key_idx.insert(Dict::value_type(keyword[i], i)); 
  }
  
const  size_t Pos_init  =   - 1 ;
  vector
< size_t >  prev_pos(keyword_sz, Pos_init);  // 上次碰到的关键字符串的位置
  std:: set < size_t >  pos;         // 对关键字符串的位置进行升序排列
  
  vector
< size_t >  old_pos;       // 分词后的字符串,在原字符串中的位置 
  old_pos.reserve(str_sz  +   1 );
  old_pos.push_back(
0 );
  
for  (size_t i  =   0 , sum  =   0 ; i  <  str_sz;  ++ i) {
    sum 
+=  str[i].size();
    old_pos.push_back(sum);
  }
   
  size_t beg 
=   0 , end  =   0 , len  =   - 1 , count  =   0 ;   // 记录结果
   for  (size_t i  =   0 ; i  <  str_sz;  ++ i) {
    
// Dict::iterator it = key_idx.find(&str[i]);  // ----
    Dict::iterator it  =  key_idx.find(str[i]);

    
if  (it  ==  key_idx.end())  continue ;
    
    assert(it
-> second  <  prev_pos.size());
    size_t
&  last_pos  =  prev_pos[it -> second];
    
if  (last_pos  !=  Pos_init)  pos.erase(last_pos);  // 更新前要先删除旧的
     else   ++ count;   // 已匹配关键字 数加1
    last_pos  =  i;
    
    pos.insert(pos.end(), i); 
    
    assert(count 
<=  keyword_sz);
    
if  (count  ==  keyword_sz) {      // 包含所有关键字
      size_t pbeg  =   * pos.begin();
      
// std::set<size_t>::iterator it = pos.end();
      
// size_t pend = *--it + 1;
      size_t pend  =  i  +   1 ;
      
      assert(pbeg 
<  old_pos.size());
      assert(pend 
<  old_pos.size());
     
      size_t plen 
=   old_pos[pend]  -  old_pos[pbeg];
      
if  (plen  <  len) {
        len 
=  plen;
        beg 
=  pbeg;
        end 
=  pend;
      }
    }
  }
  
  output(
& str[ 0 ],  & str[str_sz],  " words:     " );
  output(
& keyword[ 0 ],  & keyword[keyword_sz],  " keywords:  " "   " );
  
  
if  (beg  ==  end  &&   ! pos.empty()) {       // 没找到所有关键字
    cout  <<   " ( "   <<  pos.size()  <<   " / "   <<  keyword_sz  <<   "  Matched)  " ;
    beg 
=   * pos.begin();
    std::
set < size_t > ::iterator it  =  pos.end();
    end 
=   *-- it  +   1 ;
  }
  output(
& str[beg],  & str[end],  " result:    " );
  cout 
<<   " \n " ;
}


template
< typename T, size_t sz >  
inline size_t getsz(T (
& )[sz]) {  return  sz; }

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

  solve(str, getsz(str), keyword, getsz(keyword));   
}

 


 

转载于:https://www.cnblogs.com/flyinghearts/archive/2011/03/24/1994453.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值