有一串首尾相连的珠子,共有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);
}