将两个数组首尾相连c语言,一个百度笔试中的首尾相连的珠子问题解法

之所以说是一个解法,是因为这是我暂时能想到的线性时间复杂度的算法,需要遍历约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分享到:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值