解题笔记(29)——珠子问题

      问题描述:一串首尾相连的珠子(n个),有N种颜色(N<=10),设计一个算法,取出其中一段,要求包含所有N种颜色,并使长度最短。并分析时间复杂度与空间复杂度。

      思路:可以利用一种计数的方法。定义两个指针p1和p2,主要有三个步骤:

     (1)p1向前移动,如果p1所指的珠子颜色编号为 i ,则增加 i 的出现次数。当出现的颜色种数为N时,p1停止。

     (2)p2向前移动,如果p2所指的珠子颜色编号为 i ,则减少 i 的出现次数。当出现的颜色种数减为N-1时,p2停止。

     (3)p1和p2所指的这段珠子,包含了N种颜色。如果比当前的最小段更短,则进行更新。回到(1)继续,循环终止条件为p2指向最后一个珠子。

     

      以上给出了实现的思路,具体实现时应该注意一点,这串珠子应该是环状的。时间复杂度为O(n),空间复杂度为O(N)。

 

      参考代码:

const int N = 5; //颜色种类

//函数功能 : 找目标珠子,珠子的颜色用编号表示
//函数参数 : pBead指向珠子数组,n为珠子个数,from为开始位置,to为结束位置
//返回值 : 找到为真,否则为假
bool BeadProblem(int *pBead, int n, int &from, int &to)
{
	if(pBead == NULL || n < N)
		return false;   //参数存在问题,找不到

	int count[N] = {0};      //用于计数
	int minlen = n+1;
	int i = 0, j = -1, cnt = 0;

	while(j < n - 1) //j = n-1时,指向最后一个珠子
	{
		if(++count[pBead[i]] == 1 && ++cnt == N) //步骤1
		{
			for(j = j + 1; j < n && --count[pBead[j]]; j++) //步骤2
				;
			if(j == n) 
				j--;  //已越过最后位置,退一步

			//步骤3
			int len = (j > i) ? i-j+1+n: i-j+1; //长度计算公式
			if(len < minlen)  //更新长度,以及起始结束位置
			{
				to = i;
				from = j;
				minlen = len;
			}
			cnt--; //颜色种数减1
		}

		i = (i + 1) % n;        //考虑了环的特性,不能是i++
		if(i == 0 && j == -1)   //i回到了初始位置,但是j未更新,即没有找到符合条件的段
			break;
	}
	return minlen != n+1;
}

       有几个地方解释一下:第一,if(++count[pBead[i]] == 1 && ++cnt == N)  这个判断语句看似很复杂。具体是这样执行的,首先增加当前珠子颜色的出现次数。如果不是1,根据布尔判断的短路原则,++cnt 不会执行。如果是1,表明第一次出现,需要增加出现颜色的种数,因此执行++cnt。如果cnt增加到N表示找到了一段珠子,包含所有N种颜色。接着进入if语句,尽可能缩短。

      第二,for(j = j + 1; j < n && --count[pBead[j]]; j++) 这句for循环语句。为什么j 初始时要加1呢?可以这样理解,假设之前已经找到了一段珠子,那么j指向珠子的末端。现在又找到一段,这一段珠子的末端应该从j + 1开始计算,不应包含j。

      下面给出一段测试程序及一些测试结果。

int main()
{
	int bead[] = {3,3,4,0,1,1,2,2,3,4,0,1,1,2,3,4};	
	int from, to;
	int n = sizeof(bead)/sizeof(int);

	if(BeadProblem(bead, n, from, to))
	{
		int i;
		for(i = from; i != to; i = (i+1)%n)
			cout<<bead[i]<<' ';
		cout<<bead[i]<<endl;
	}
	return 0;
}
//            输入                         输出
//{0,1,2,3,4}                        {0,1,2,3,4}
//{1,1,3,3,1,0,3,4,1,1,1,2,2,3,4}    {0,3,4,1,1,1,2}
//{1,0,1,3,3,1,0,3,4,1,1,1,2,2,3,4}  {2,3,4,1,0}
//{1,1,0,3,1,2,3,4,1,1,1,2,2,3,4}    {0,3,1,2,3,4}
//{3,3,4,0,1,1,2,2,3,4,0,1,1,2,3,4}  {2,3,4,0,1}
//{1,1,3,3,1,1,3,4,1,1,1,2,2,3,4}     找不到

       本人享有博客文章的版权,转载请标明出处 http://blog.csdn.net/wuzhekai1985



猜你喜欢

转载自blog.csdn.net/wuzhekai1985/article/details/6723589