如何从 int Kotlin 数组中获得最长的连胜

How to get longest streak from an array of int Kotlin

我正在写一个故事,想知道如何解决这个类似于我当前工作的问题,因此每周连续运动被定义为一个或多个连续的活跃周,用户在其中进行了锻炼。所以假设给定了一组活跃用户天数,我们需要找出最长的每周活跃用户天数的长度。 这里数组中的每个元素代表日历中任意星期一的偏移量(例如,2022 年 2 月 4 日,星期四)。

偏移量是零索引的。 一些有用的提示是:

这是我的代码:

val str1 = arrayOf(9, 14, 365, 366, 367) //2
val str2 = arrayOf(2, 22, 29, 37, 43, 366, 391) //4

fun findLongestStreak(callDays: Array<Int>): Int{

}

卡了一个星期的这个函数最简单的写法是什么?

你可以这样做

val str1 = arrayOf(9, 14, 365, 366, 367) //2
val str2 = arrayOf(2, 22, 29, 37, 43, 366, 391) //4

fun toWeek(day: Int) = day / 7
fun activeWeeks(days: Array<Int>) = days.map(::toWeek).toSet().sorted()

// find runs of consecutive numbers in a list of integers,
// and return a list of those runs' lengths
fun getStreaks(nums: List<Int>): List<Int> = 
    if (nums.isEmpty()) emptyList()
    // We're going to iterate over each adjacent pair of numbers, so we can check
    // if the second number immediately follows the first, so we can add to
    // the current streak.
    
    // Since we're looking at the second number in each pair, the first in the list
    // (we know there's at least one) is a special case - so we start the list
    // we're building up with a streak of 1, representing that first number
    else nums.zipWithNext().fold(mutableListOf(1)) { streaks, (prev, current) ->
        streaks.apply {
            // if the second number (the one we're checking) follows the first,
            // add 1 to the most recent streak length in the list
            if (current == prev + 1) streaks[lastIndex] += 1
            // if they're not consecutive, we need to start a new streak for this number
            else streaks.add(1)
        }
    }

fun findLongestStreak(callDays: Array<Int>): Int =
    callDays.run(::activeWeeks)
        .run(::getStreaks)
        .run { maxOrNull() ?: 0 }

这主要是解释 - 基本上我使用 toSet() 将所有 天数 转换为相应的 周数 删除重复项并 sorted 将其转换为 List 升序 周数

然后 fold 函数从第一个 周数 的连续 1 开始,然后完成其余部分其中,将每个与前 周数 进行比较。如果是连续的,将 1 添加到当前连胜。如果不是,则开始一个长度为 1 的新连胜。 (如果你不喜欢 fold,你可以用相同的逻辑做一个 for 循环)

这样你就得到了一个连胜长度列表,你可以选择最长的那个

您可以使用具有锻炼周唯一值的 Set。然后用 non-consecutive 周过滤集合(想法是对于任意一天,它不应该属于刚好等于前一天的周 + 1 的周);

最终,结果的大小就是您所需要的(活跃的离散周):

更新:

使用 toSet() 简化而不是 for 循环更线性:

fun findLongestStreak(callDays: Array<Int>) = callDays.map { it / 7 }.toSet()
    .let { it.filter { day -> !it.contains(day - 1) } }.size

原回答:

fun findLongestStreak(callDays: Array<Int>): Int {
    val workoutWeeks = mutableSetOf<Int>() // all the workout weeks
    for (day in callDays) workoutWeeks.add(day / 7)
    return workoutWeeks.filter { !workoutWeeks.contains(it - 1) }.size
}

请注意,day / 7 returns 周数偏移同样指的是它们相对于基准星期一的偏移。

该代码基于以下想法:连续周减去它们在数组中的位置索引会导致相同(无意义)的数字,而对于两个 non-consecutive 周,这两个数字将不同:

val str1 = arrayOf(9, 14, 365, 366, 367)
val str2 = arrayOf(2, 22, 29, 37, 43, 366, 391)

fun findLongestStreak(callDays: Array<Int>): Int {
  return callDays
    .map { it / 7 }                         // [1, 2, 52, 52, 52]
    .toSet()                                // [1, 2, 52]
    .mapIndexed { index, i -> i - index }   // [1, 1, 50]
    .groupBy { it }                         // {1=[1, 1], 50=[50]}
    .maxOf { it.value.count() }             // 2
}

println(findLongestStreak(str1))   // 2
println(findLongestStreak(str2))   // 4