最短摘要问题

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

么,这段摘要是怎么产生的呢?简化一:
  1.  假设给定的已经是经过网页分词之后的结果,词语序列数组为W。其中W[0], W[1],…, W[N]为一些已经分好的词语。
  1. 假设用户输入的搜索关键词为数组Q。其中Q[0], Q[1],…, Q[m]为所有输入的搜索关键词。
这样,生成的最短摘要实际上就是一串相互联系的分词序列。比如从W[i]到W[j],其中,0<i<j<=N。例如上图所示的摘要“程序员面试、算法研究、编程艺术、红黑树4大经典原创吸了集锦与总结 作者:July--结构之法算法之道blog之博主.....”中包含了关键字——“结构之法”。    OK,如何解决这样的问题呢?有没有感觉一头雾水?先看一个例子,碰到问题不要着急,可以举一些例子来找思路 分词数组W:w0,w1,w2,w3,q0,w4,w5,q1,w6,w7,w8,q0,w9,q1; 关键字数组Q:qo,q1; 最短摘要数组Seq; 此时最短摘要应该是q0,w9,q1; 我们是如何得到的呢? 第一次遍历找到包含全部关键字的摘要得到w0,w1,w2,w3,q0,w4,w5,q1更新Seq; 第二次遍历找到包含全部关键字的摘要得到w1,w2,w3,q0,w4,w5,q1更新Seq; …… 继续遍历找到包含全部关键字的摘要得到q0,w4,w5,q1更新Seq; …… 继续遍历找到包含全部关键字的摘要得到w5,q1,w6,w7,w8,q0更新Seq; 继续遍历找到包含全部关键字的摘要得到q1,w6,w7,w8,q0更新Seq; 继续遍历找到包含全部关键字的摘要得到w6,w7,w8,q0,w9,q1;;更新Seq; …… 最后一次遍历找到包含全部关键字的摘要得到q0,w9,q1;更新Seq; 明白了倒是明白了,可是这复杂度是不是很高? 总是重复地循环好多都是不必要的无用功。     我们可以降低复杂度吗? 我们可以这么做:      第一次遍历找到包含全部关键字的摘要得到w0,w1,w2,w3,q0,w4,w5,q1更新Seq;      此时不扫描遍历了,更新begin指针,使之向前。直到得到q0,w4,w5,q1更新Seq;      begin继续向前导致w4,w5,q1,不满足。此时需要end指针向后遍历。      继续遍历找到包含全部关键字的摘要得到w4,w5,q1,w6,w7,w8,q0更新Seq;      此时不扫描遍历了,更新begin指针,使之向前。直到得到q1,w6,w7,w8,q0更新Seq;      begin继续向前导致w6,w7,w8,q0,不满足。此时需要end指针向后遍历。      第三次遍历找到包含全部关键字的摘要得到w6,w7,w8,q0,w9,q1更新Seq;      此时不扫描遍历了,更新begin指针,使之向前。直到得到q0,w9,q1更新Seq;      begin继续向前导致w9,q1,不满足。此时需要end指针向后遍历。知道end=N; 每次遍历结束后我们不从下一个分词开始,我们从得到的包含数组的第一个关键字的后面开始遍历。这样能省很多劲呢。 在遍历的过程当中我们需要保存最短摘要的长度,和起始位置。 编码实现

bool IsAllContain(int key[],int N,int title[],int begin,int end); 
//判断begin-end是否包含全部关键字
int main()
{
	int key[N]={14,17};
	int title[M]={0,1,2,3,14,5,6,17,8,9,10,11,17,13,14}; 
	int TargetLen=M+1;     //初始化摘要长度大于文章的最长长度
	int begin=0;      //扫描指针
	int end=0;            //扫描指针
	int AbstractBegin=0;           // 目标摘要的起始地址  
	int AbstractEnd=0;           // 目标摘要的结束地址  
	while(true)
	{
		while((0==IsAllContain(key,N,title,begin,end))&&end<M)
		{	//如果不包含全部关键字继续使end向前
			end++;    
		}
		while(IsAllContain(key,N,title,begin,end)) 
		{		//如果包含全部关键字继续使begin向前,同时更新最短摘要
			if(end-begin+1<TargetLen)  
			{
				TargetLen=end-begin+1;
				AbstractBegin=begin;
				AbstractEnd=end;
				
			}
			begin++;
		}
		if(end>=M)  
			break;
	}
	
	cout<<"AbstractBegin="<<AbstractBegin<<"  AbstractEnd="<<AbstractEnd<<endl;
	cout<<"TargetLen="<<TargetLen<<endl;
	
	return 0;
} 

bool IsAllContain(int key[],int N,int title[],int begin,int end)  //判断begin-end是否包含全部关键字
{
	int num=0;                    //只能是双重循环,因为key与title的不是一致顺序
	for(int i=0;i<N;i++)
	{
		for(int j=begin;j<=end;j++)
		{
			if(title[j]==key[i])
			{
				num++;  //每一次循环得到一个不同的关键字足够了
				break;
			}
		}
	}
	if(num==N)
		return true;
	else
		return false;
}


 算法明白了之后,代码就很好懂了。
 时间复杂度不敢说很好了,但是比那个复杂度好好很多了。。
转载请注明出处http://blog.csdn.net/sustliangbo/article/details/9277485

你可能感兴趣的:(算法,编程之美)