一道百度和大众点评的题目,网上可找到代码,应该是对的,可是注释少,所以像我这样的菜鸟还是比较难一目看懂的,所以花了点时间添加了注释,便于阅读。
#include <iostream>
using namespace std;
#define MAXN 10
int colors[MAXN];//record the counter of one color
int colorsCounter;
void find(int arr[],int len, int colorsNeed) //输入参数为数组、长度m、颜色数n
{
int bestStartIndex = 0;//最佳长度开始位置
int bestLen = len;//最佳长度,初始化为m
int lastStartIndex = 0;//子串开始位置
int firstFoundEnoughPearls = len;
/*这里求的是没有绕过环的最小子串长度*/
for ( int i=0; i<len; ++i) //从头开始遍历
{
if (!colors[arr[i]])//该颜色尚未出现过
colorsCounter++;//总颜色计数 只要没新颜色出现,其值不变
colors[arr[i]]++;//对该颜色计数
if (colorsCounter==colorsNeed)//凑齐颜色了 一旦凑齐以后每次循环都成立
{
firstFoundEnoughPearls = i;//记录当前位置
int j = lastStartIndex;//记录子串开始位置
while (colors[arr[j]]>1) //该颜色数目大于1,即出现了多次
{
colors[arr[j]]--;//减掉一次
++j;//处理下一个
}//知道某颜色刚好出现了一次,也就是开始位置了
if (i-j+1<bestLen)//子串长度小于最佳长度
{
bestStartIndex = j;//最佳开始位置
bestLen = i-j+1;//最佳长度
if (bestLen==colorsNeed)//最佳长度刚好等于颜色数,不会更短的长度了
break;//果断跳出来
}
lastStartIndex = j;//子串开始位置更新为j
}
}
//cout<<firstFoundEnoughPearls<<endl; //26
/*这里求的是绕过尾部的子串,然后到当前开始地方的递归*/
for (i=0; i<firstFoundEnoughPearls; ++i)
{
if (!colors[arr[i]])
colorsCounter++;
colors[arr[i]]++;
int j = lastStartIndex;
while (colors[arr[j]]>1) //去掉了上面的if条件,从第一次满足之后都是满足的
{
colors[arr[j]]--;
++j;
}
if (i-j+1+len<bestLen) //len-j+i+1为j和i之间环绕的长度
{
bestStartIndex = j;
bestLen = i-j+1+len;
if (bestLen==colorsNeed)
break;
}
lastStartIndex = j;//下个子串开始的地方
}
int offset = bestStartIndex;//最佳开始位置
cout<<"bestStartIndex = "<<bestStartIndex<<endl;
for (i=0; i<bestLen;) //绕环打印,只能赞了~~~
{
cout << arr[i+offset] << " ";//先打印数组后面的
++i;//在这里做++
if (i+offset>=len)//超出了,该打印数组前面的了
offset = 0-i;//从下标0出开始打印,反正根据循环不会打印超过bestLen个
}
cout << endl;
}
int main()
{
int arr[] = {1,2,3,3,2,1,4,1,2,3,2,6,4,5,2,6,2,3,4,1,2,5,2,3,4,5,6};
int m = sizeof(arr)/sizeof(arr[0]);
//cout<<m<<endl;//27
int n = 6;
find(arr,m,n);
return 0;
}