分享百度一个面试题:
有一串珠子首尾不相连,总共N个,这些珠子总共有M种颜色,在这串珠子里面找到最短的连续珠子子串,使子串出现所有的M种颜色。
毫无疑问,这个可以用暴力破解,N^2 时间效率可以解决,面试官百分之一千会让你优化。
设置两个位置指针 t 和 h ,代表出现所有颜色的联系珠子的子串的起始位置和最后位置
如果能保证 h 所指的位置的珠子是第一次出现,如果尽量将 t 向前移动,得到的 t - h 的子串就是以 h 结尾子串的最短子串
在什么情况下,能向前移动 t 呢?
如果说 t 位置所指的颜色,在 t+1~h之间还有出现,就能保证 t 可以向前移一个位置
比如珠子是:11232324314,最短子串是 2431
第一次找到所有颜色的子串是 11232324,子串最后颜色 4 是子串中唯一出现的颜色
此时的 t 指向 第一个 1,而 1 在后面还有出现,可以将 t 向前移动一个 即得到更短的子串 1232324
还能再移吗?不能了,因为此时 颜色 1 在这个子串中唯一,也就是说 以 h(指向子串最后一个4)结尾的子串,要包含所有颜色1234那么最短的子串是 1232324
得到的这个子串是所有包含所有颜色子串最短的吗?也不是,那么我们该怎么找到下一个包含所有颜色的子串?
假设,我删除掉获得的子串 1232324 的第一个 颜色 1,再从后边的珠子里找到颜色 1 ,即 23232431,此时 最后的颜色 1 又是这个子串唯一的颜色了
同样的方法,只要后面还有和 当前 t 指向的颜色,那么就移动 t 直到无法移动,过程如下:
t 指向 23232431,2 在后面的 3232431 中还有出现,向前移动 t
t 指向 3232431,3 在后面的 232431 中还有出现,向前移动 1
t 指向 232431,2 在后面的 32431 中还有出现,向前移动 1
t 指向 32431,3 在后面的2431 中还有出现,向前移动 1
t指向 2431,2 在后面的431中没有出现了,不能向前移动了
此时得到的子串就是最短子串,因为 子串长度==颜色种数
给出代码:
//M长度队列有N种颜色,找到包含所有颜色的最短字串 int FindFullColor(int arr[],int n,int m){ //C用来存储 每一种颜色出现的次数,C[0]不用 //颜色用m来表示,m代表有颜色:1,2,3,...,m //即在arr[n]中找出同时出现 1,2,...,m 的最短字串 int *c=(int *)malloc((m+1)*sizeof(int)); memset(c,0,(m+1)*sizeof(int)); int min=n; int h=0,t=0,ctr=m; while(1){ while( ctr>0 && h<n ){ if(c[arr[h]]==0){ ctr--; } c[arr[h]]++; h++; } //已经结束但依然没找齐颜色 if(h>=n){ break; } while(1){ c[arr[t]]--; if(c[arr[t]]==0){ break; } //如果c[arr[t]]--之后,不为0,代表在 t 后面还有 arr[t] 的颜色 //所以当前 t 不是最短的,至少比后面那个长 t++; } if(h-t<min){ min=h-t; //更新之后打印 //如果最后只打印最短的,则可以把 min,t,h 同时存储 for(int i=t;i<h;i++){ printf("%d",arr[i]); } printf("\n"); } //删除颜色直到某个颜色为,这个颜色在 t-h 之间没有了 //这就需要在 h-n 之间再找到这种颜色 t++; ctr++; } free(c); c=NULL; return min; }