一串首尾相连的珠子共m个,其中有n中颜色,求出包含全部这n种颜色的最短的串。

一道百度和大众点评的题目,网上可找到代码,应该是对的,可是注释少,所以像我这样的菜鸟还是比较难一目看懂的,所以花了点时间添加了注释,便于阅读。

#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;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值