m个珠子共n种颜色,找出包含n种颜色的最短连续片段

这道题的暴力解法找到所有包含n中颜色的珠子连续片段,然后比较片段长短找到最短的片段。

这种方法的时间复杂度是O(nm^2)


当然这种方法是可以优化的,优化的空间就在于比已经找到的包含所有颜色连续片段大的片段,因为这些片段是不需要考虑的。

所以可以通过如下的方法,以O(mn)的时间复杂度得到问题的解:

1. 从珠子的一端开始,先找到一个包含所有颜色的连续片段,并在统计时记下每个颜色出现的次数;

2. 精简这个片段,使其长度达到最小;

  • 从这个片段的起始端开始检查,如果这个位置起始端的颜色个数大于1,则将连续片段的起始端向后加1,
  • 否则转步骤3

3. 向后移动连续片段的尾端,转步骤2.


具体代码如下(在代码中将颜色数量设置为了3):

#include "stdafx.h"
#include <sstream>
#include <fstream>
#include <iostream>


bool HasAllColor(int *color, int color_num) {
    bool ret = true;
    for (int k = 0; k < color_num; ++k) {
        if (color[k] == 0) {
            ret = false;
            break;
        }
    }
    return ret;
}


//如果是暴力解法,会找到所有全色子串进行长度比较,这样的算法复杂度应该是O(n2)
//优化的思想就是对于确定比已找到的全颜色子串长的不予考虑
void FindShortestFullString(int *arr, int arr_size, int &start, int &end) {
    //先找到第一个包含左右颜色的子串,并记录子串个数,然后前后移动首尾指针,找到最小子串
    //找到第一个包含所有颜色的子串
    int color[3] = {0};
    int k = 0;
    for (k = 0; k < arr_size; ++k) {
        ++color[arr[k]];
        if (HasAllColor(color, 3)) {
            break;
        }
    }
    
    int tmp_start = 0;
    int tmp_end = k + 1;
    start = tmp_start;
    end = tmp_end;
    //每次包含所有颜色的子串的开始端更新后,都要根据color对子串进行精简
    //因为串不能断,所以要精简到首部是当前子串中唯一对应颜色的元素
    for (int i = tmp_start; i < tmp_end && color[arr[i]] > 1; ++i) {
        --color[arr[i]];
        ++tmp_start;
    }
    for (++k; k < arr_size; ++k) {
        tmp_end = k + 1;
        ++color[arr[k]];
        //每次包含所有颜色的子串的开始端更新后,都要根据color对子串进行精简
        //因为串不能断,所以要精简到首部是当前子串中唯一对应颜色的元素
        for (int i = tmp_start; i < tmp_end && color[arr[i]] > 1; ++i) {
            --color[arr[i]];
            ++tmp_start;
        }
        if (tmp_end - tmp_start < end - start) {
            start = tmp_start;
            end = tmp_end;
        }
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    //int arr[11] = {0,2,2,1,2,1,0,1,0,2 ,0};
    int arr[11] = {0,1,2,0,1,2,0,2,1,1,0};
    //int arr[11] = {0,1,0,0,0,0,0,2,1,1,2};
    int start = -1;
    int end = -1;
    FindShortestFullString(arr, 11, start, end);
    std::cout << start << " " << end << std::endl;


<span style="white-space:pre">	</span>return 0;
}



对于上述算法正确性的分析:

在上面的代码中,连续片段的尾端遍历了所有珠子,也就是说以任何一个珠子结尾的连续片段都已经被分析过了,对每一个尾端不同的连续片段进行片段精简就是为了避免对更长连续片段的考察。

所以上述代码比较了以各个珠子作为尾端时的最短连续片段,并找出这些最短连续片段中最短的,因此这个最短的连续片段必然是全局最短的。


时间复杂度分析:

在上述代码中,连续片段的尾端指针(代码中的k)是按照顺序从头走到尾,遍历了m次,而每一次k移动时,i都会跟着调整(为了精简连续片段),但是这个调整都是向前不会有倒退的情况出现,并且i是不会与k相遇的,因此i的遍历次数是小于k 的,所以肯定小于m。

所以算法的时间复杂度为O(mn)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值