有一串首尾相连的珠子,共有m个,每一个珠子有一种颜色,并且颜色的总数不超过n(n<=10),求连续的珠子的颜色总数为n时,长度最小的区间。...

有一串首尾相连的珠子,共有m个,每一个珠子有一种颜色,并且颜色的总数不超过n(n<=10),求连续的珠子的颜色总数为n时,长度最小的区间。可简述思路或者给出伪代码,并且给出时间和空间复杂度分析。(baidu2011校园招聘笔试题目)

分析:首先要为这个题目构建数据结构,如何表示n种颜色?可以用1到n的数来表示相应的颜色,把它存放在一个数组中。这样,题目就变为有m个数,取值范围1到n,求最小子段,使得该段中包含取值范围为1到n的所有的数。该段中有的数可能重复出现。最理想的情况是,该子段恰好包含n个数。

归纳:知道数组arr[1….i-1]中的最小子段,现在来考虑第i个数的加入会构成什么影响。分两种情况,最小子段已i-1结尾;最小子段不已i-1结尾,那么我们需要知道些什么信息以对i进行运算?也就是说假设i前面的处理过程已经记录了一些信息,可以对处理第i个数有帮助,现在的问题变我们需要记录什么样的信息?想来想去,好像陷入了一个误区,以至于找不到解。如果真的面试,那就挂了。

逆向思考:从前往后推,得不到答案,那我们试试从后往前推。现在问题变为从第i个数往前找一个我们需要的子段,然后比较两个子段的长度,取最小值。

缺点:每往后处理一个数,都要往前扫描来找到一个以i结尾的子段,这样未免效率太低了吧!而且不是每个以i结尾的数都构成一个子段,我们真的需要每次都往前扫描吗?

改进:我们考虑第i个数,当第i个数有什么样的特征,我们就确定又有一个子段出现?假设以i结尾的数一定构成一个子段,那么这个子段和前面的子段构成什么样的关系?

再归纳:知道数组arr[1….i-1]中以i-1结尾的最小子段。为什么可以这样归纳,因为我们始终可以从前往后找到第一个子段。在纸上画画,发现不是每个后面的数都需要处理。假设这个子段是AB(A开头,B结尾),那么在后面找到A,我们就可以确定一定又包含了一个子段。因为ABA中一定包含两个子段,现在问题已经解决。

我们知道了怎么样求AB的子段,假设K为ABA中的某一位置,现在问题变为怎样计算KA的子段,然后比较两个子段的大小,取最小值。

解决:用一个长度为n的数组来统计每种颜色出现的最新的位置,数组下标对应相应的颜色,当这个数组第一次填满时,我们已经找到了包含n中颜色的子段。取数组中的最大最小值之差加一即为所求。后面当找到A时,我们确定一定存在子段,这个数组记录着该子段中每种颜色的最新消息。我们还可以进行优化,当最小段长度为n时,就结束扫描。

复杂度分析:只需要扫描一遍数组,每次找到子段时,还需要在颜色数组中查找最小位置和最大位置。如果处理颜色数组忽略不计,那么这个复杂度为O(n)。看下面代码:

int getmin(int *arr,int len) { if(arr == NULL || len < 1) return -1; int min = arr[0]; for(int i = 1; i < len; ++i) { if( arr[i] < min) min = arr[i]; } return min; } int getmax(int *arr,int len) { if(arr == NULL || len < 1) return -1; int max = arr[0]; for(int i = 1; i < len; ++i) { if( arr[i] > max) max = arr[i]; } return max; } int minlength(int *arr, int len, int colors) { if(len < colors) return -1; int *tarr = new int [colors]; int curcolors=0; int maxposition=0; int minposition=len; int minlen = 0; memset(tarr,-1,sizeof(int)*colors); int index=0; /* 查找第一次出现的子段 */ for(;index<len;++index) { if( tarr[ arr[index] -1 ] < 0) ++curcolors; tarr[ arr[index]-1 ] = index; //颜色数组 if(curcolors == colors) { minposition = getmin(tarr,colors); maxposition = getmax(tarr,colors); minlen = maxposition - minposition + 1; break; } } int minp = 0; int maxp = 0; for(++index;index < len; ++index) { /* 这样是上面提到的再次找到A */ if( tarr[ arr[index]-1 ] == minposition) { tarr[ arr[index]-1 ] = index; minp = getmin(tarr,colors); maxp = getmax(tarr,colors); if(maxposition - minposition + 1 < minlen) { minlen = maxposition - minposition + 1; minposition = minp; maxposition = maxp; } continue; } tarr[ arr[index]-1 ] = index; } cout<<"minposition: "<<minposition<<endl; cout<<"maxposition: "<<maxposition<<endl; cout<<"len : "<<minlen<<endl; return minlen; } int main(int argc, char **argv) { int arr[] = {1,2,2,3,1,3,4,2,3,5,4,2,3,2,4,3,2,1,2,1,4,5,2,2,2,2,3}; int minlen = minlength(arr,27,5); for(;;); return(0); }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值