之所以说是一个解法,是因为这是我暂时能想到的线性时间复杂度的算法,需要遍历约2m遍,这里把算法列出来,仅供参考。
题目:
有一串首尾相连的珠子,共有m个,每一个珠子有一种颜色,并且颜色的总数不超过n(n<=10),求连续的珠子的颜色总数为n时,长度最小的区间。可简述思路或者给出伪代码,并且给出时间和空间复杂度分析。(baidu 2011校园招聘笔试题目)
拿到这个题目,我首先想到的是暴力搜索。即对每个珠子,都搜索以该珠子为结尾的最短符合题意的序列。但是这样比较的时间复杂度特别高,应该是m的指数,显然是不可取的,通过简单的分析我们能够发现,在搜索过程中,我们进行了许多冗余的比较。本着空间换时间的想法,我们有没有办法把这些比较记录下来,然后去掉这些冗余呢?答案是肯定的。
我想到的一个算法,里面用到了一些动态规划的思想,可以在线性时间内完成题目。首先为了方便描述,让我们假设珠子不是首尾相连。让我们以数组a[m]来表示珠子的序列,用1-n数字来表示颜色,则算法思路如下:
定义一个数组least[m];对于a[i]的元素,least[i]表示珠子序列中以珠子a[i]为结尾的包含所有颜色的最短序列的长度;
定义一个数组save[n+1]; 这个数组的元素save[i]表示颜色i上一次出现的位置,初始化为-1;注意这里的i表示颜色而不是珠子!
定义一个数组next[m];对于a[i]的元素,next[i]表示当前珠子的颜色下一次出现的位置;初始化为-1;
定义两个指针 i 和 j ,初始化为0,即指向a[0];
首先遍历一遍a[m],将每个珠子的颜色下一次出现的位置填充到next[i]中;具体做法是利用数组save[n+1]保存颜色c上一次出现的位置d,当再次出现c时,将next[d]设置为当前位置。这部分可以在O(m)时间内完成,具体代码中还可以优化。
将指针j向右滑动
判断 i
如果j = n,则遍历完成,找到least[m]中最小的数的位置,则就能找到符合题意的最小序列。
当然,我们需要考虑到珠子首尾相连。我们可以让j=n之后,从0开始重新循环,直到 i=n 或 j到达位置s,位置s表示从a[0]开始,第一个出现所有颜色的位置。
下面是我使用C语言实现的代码,在实际代码中,next[j]我是一边挪动 j 一边填充的。代码如下:
#include
#define CNUMBER 4 //颜色的个数
int find(int *a, int n){
int number = CNUMBER; //定义一个计数器,目的是找到上面所说的第一个出现所有颜色的位置s
int save[number+1]; //颜色上一次出现的位置
int next[n]; //元素颜色下一次出现的位置
int least[n]; //以该元素结尾的最短序列
int i, j;
for(i=0;i
save[i]=-1;
}
for(i=0;i
next[i]=-1;
}
i = 0;
for(j=0;j
if(number!=0)
least[j]=-1; //将还没有出现所有颜色的位置的least设为-1,下面会用到
if(save[a[j]]==-1){ //save[i]为-1表明该颜色i第一次出现
save[a[j]]=j;
number--;
}
else{
next[save[a[j]]]=j; //设置next
save[a[j]]=j; //把save[i]设为当前位置,用于下一次使用
}
while(i
if(a[i]==a[j]||next[i]!=-1) //i++的条件
i++;
else
break;
}
if(number == 0 )
least[j]=j-i+1; //计算least
}
j=0;
while(least[j]==-1 &&i
next[save[a[j]]]=j;
while(i
if(a[i]==a[j]||(next[i]>i||next[i]
//next[i]>i||next[i]
i++;
else
break;
}
least[j]=j+n-i+1;
j++;
}
for(i=0;i
printf("%d ", least[i]); //我这里最终将所有least打印出来,可以更改下让把符合题意的序列打印出来
printf("\n");
}
int main(){
int n;
int a[100];
scanf("%d", &n); //n表示珠子的长度
int i;
for(i=0;i
scanf("%d", &a[i]); //输入珠子
find(a, n);
}
#include
#define CNUMBER 4 //颜色的个数
int find(int *a, int n){
int number = CNUMBER; //定义一个计数器,目的是找到上面所说的第一个出现所有颜色的位置s
int save[number+1]; //颜色上一次出现的位置
int next[n]; //元素颜色下一次出现的位置
int least[n]; //以该元素结尾的最短序列
int i, j;
for(i=0;i
save[i]=-1;
}
for(i=0;i
next[i]=-1;
}
i = 0;
for(j=0;j
if(number!=0)
least[j]=-1; //将还没有出现所有颜色的位置的least设为-1,下面会用到
if(save[a[j]]==-1){ //save[i]为-1表明该颜色i第一次出现
save[a[j]]=j;
number--;
}
else{
next[save[a[j]]]=j; //设置next
save[a[j]]=j; //把save[i]设为当前位置,用于下一次使用
}
while(i
if(a[i]==a[j]||next[i]!=-1) //i++的条件
i++;
else
break;
}
if(number == 0 )
least[j]=j-i+1; //计算least
}
j=0;
while(least[j]==-1 &&i
next[save[a[j]]]=j;
while(i
if(a[i]==a[j]||(next[i]>i||next[i]
//next[i]>i||next[i]
i++;
else
break;
}
least[j]=j+n-i+1;
j++;
}
for(i=0;i
printf("%d ", least[i]); //我这里最终将所有least打印出来,可以更改下让把符合题意的序列打印出来
printf("\n");
}
int main(){
int n;
int a[100];
scanf("%d", &n); //n表示珠子的长度
int i;
for(i=0;i
scanf("%d", &a[i]); //输入珠子
find(a, n);
}
当然,这个算法未必就是一个很好的算法,如果你有更好的方法,欢迎留言讨论。
anyShare分享到: