搜狐笔试:Kolakoski sequence

写在前面:

  昨晚本来是帮师姐做笔试题的,结果gg了,然后网上搜了一下,相关的资料和实现也比较少,所以决定总结一下,共同学习。欢迎各位交流想法,提意见。

题目描述:

  偷懒,直接放的图片_

image

  要求:给定一数组,输出其构造的Kolakoski序列的前n项。

背景知识介绍:

百度百科介绍

  Kolakoski序列是一个仅由1和2组成的无限数列,是一种通过“自描述”来定义的数列。他在整数数列大全网站上排名第二位,足见该数列在组合数学界中的重要性。他的前几项为:1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,…

  它的定义很简单,若把数列中相同的数定为一组,令a(1)=1,a(2)=2,则a(n)等于第n组数的长度。可以根据这个定义来推算第三项以后的数:例如由于a(2)=2,因此第2组数的长度是2,因此a(3)=2,;由于a(3)=2,所以第三组数的长度是2,因此a(4)=a(5)=1;由于a(4)=1,a(5)=1,所以第四组数和第五组数的长度都为1,因此a(6)=2,a(7)=1,以此类推。

  所以我们很容易发现:Kolakoski序列是一个分形数列:即将数列中相邻的数字以其个数合并,得到的仍将是数列本身。

维基百科介绍

  翻译自wikipedia:Kolakoski sequence原文介绍

  以数组[1,2]来构造的Kolakoski序列:
  1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,… (sequence A000002 in the OEIS) 是最经典的Kolakoski序列。它的“自生成”过程可以通过下图进行描述!!!

image

  当然,除此之外,还有很多这种序列,比如[1,2,3,4],它生成的Kolakoski序列为:1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 2, 3, 4, 4, 1, 1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 2, 3, 3, 4, 4, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 3, 4, 1, 1,… (sequence A079730 in the OEIS)

序列分析:

  我们可以看到,这个序列是由给定数组的数交替组成,关键在于求每个数的个数!通过观察上面的例子,发现分组之后每组的数字个数放在一起仍然是原数列,也就是说当前分组的个数是序列前边的某个数。换句话说,每个数字本身就是长度。那么就需要有两个索引变量,一个是Kolakoski序列索引count,一个是给定数组的索引。

  当前Kolakoski序列的第i个相同数字构成的子串的长度将成为变换后的Kolakoski序列中的第i个数字(也就是多对一),换而言之,变换后的Kolakoski序列的数对应变换前的Kolakoski序列的子串长度。

算法分析:

  我认为该序列最本质的特性,每个数字不仅是数字,还是相同数字的长度。

  以[1,2,3,4]构造Kolakoski序列为例:它的前几项为1, 2, 2, 3, 3, 4, 4, 4, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,…

  设n1 = 1,n2 = 2,n3 = 3,n4 = 4

  1. 因为还没有数据输出,所以置第一个输出为x1 = n1 = 1,即输出数字1的“1个拷贝”
  2. 同样第二个数字也没有输出,所以置第二个输出为x2 = n2 = 2,即输出数字2的“2个拷贝”
  3. 因为上一步输出了“两个2”,所以置第三个输出为x3 = 2,即输出数字3的“2个拷贝”
  4. 置第四个输出为x4 = 3,即输出数字4的“3个拷贝”
  5. 置第五个输出为x5 = 3,此时因为原始数组的数字已经过完一遍,所以再从头开始,即输出数字1的“3个拷贝”
  6. 置第六个输出为x6 = 4,即输出数字2的“4个拷贝”
  7. 以此类推。。。

代码实现:

完整源代码下载


public static ArrayList<Integer> solution(int[] array, int n) {
	int m = array.length;
	ArrayList<Integer> res = new ArrayList<>();
	int k = 0; // 用于标志 array 数组索引
	int count = 0; // 用于计数,控制循环次数
	while (count < n) {
		if (k == m) // 当数组里的数字都已经出现过一遍时,需要从头再开始
			k = 0;
		res.add(array[k]); // 每次先将第一个不同的数添加
		// System.out.println("" + res.get(count));
		// 关键就是控制下面循环添加的次数,因为该序列的数字,本身就是相邻相同数字的个数
		for (int i = 0; i < res.get(count) - 1; i++) {
			res.add(array[k]);
		}
		k++;
		count++;
	}
	// for (k = 0; k < n; k++)
	// System.out.println("" + res.get(k));
	// 如果要求 Kolakoski 序列的第 x 项,构造完后 res.get(x); 即可
	return res;
}

——乐于分享,共同进步,欢迎补充
——Any comments greatly appreciated
——诚心欢迎各位交流讨论!QQ:1138517609
——CSDN:https://blog.csdn.net/u011489043
——简书:https://www.jianshu.com/u/4968682d58d1
——GitHub:https://github.com/selfconzrr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BugFree_张瑞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值