简体   繁体   English

最多应用一次交换操作以获得严格增加的序列

[英]Apply Swap Operation at Most Once to get a Strictly Increasing Sequence

I am doing some of these DS&A questions on various sites online for practice and I ran into this one:我正在各种在线网站上做一些这些 DS&A 问题以进行练习,我遇到了这个问题:

Given an array of non-negative integers numbers, you are allowed to choose any number from this array and swap any two digits in it.给定一个非负整数数组,你可以从这个数组中选择任何数字并交换其中的任何两个数字。 If after the swap operation the number contains leading zeros, they can be omitted and not considered.如果在交换操作之后数字包含前导零,则可以省略而不考虑它们。 Your task is to check whether it is possible to apply the swap operation at most once, so that the elements of the resulting array are strictly increasing.您的任务是检查是否可以最多应用一次交换操作,以便结果数组的元素严格递增。

For some reason this had me stumped at first so I went to the gym and thought about it for a while.出于某种原因,这让我一开始感到难过,所以我去了健身房,想了一会儿。 Eventually I came back to it and tried again, this time coming up with a solution.最终我回到了它并再次尝试,这次想出了一个解决方案。 Unfortunately the solution I got is sort of hideous and clunky and I get the feeling this is not a good way to solve this problem.不幸的是,我得到的解决方案有点丑陋和笨拙,我觉得这不是解决这个问题的好方法。 It works with the tests I have tried but I really want to find a better solution for this.它适用于我尝试过的测试,但我真的想为此找到更好的解决方案。 I feel like I must be missing some basic thing that improve it.我觉得我必须缺少一些改进它的基本东西。

So I am posting my code here hoping for some feedback on it.所以我在这里发布我的代码,希望得到一些反馈。

def solution(numbers):

    swaps = 0
    index = 0
    for i in range(len(numbers)-1):
        if numbers[i] >= numbers[i+1]:
            swaps+=1
            index=i
    if swaps > 1:
        return False
    elif not swaps:
        return True

    swappedI = swapToSmallest(numbers[index])
    if (index-1 < 0 or numbers[index-1] < swappedI) and numbers[index+1] > swappedI:
        return True
    swappedIPlus1 = swapToSmallest(numbers[index])
    if (index+1 > len(numbers) or numbers[index+1] < swappedIPlus1) and numbers[index-1] < swappedIPlus1:
        return True

    return False
    

def swapToSmallest(num) -> int:
    num = str(num)
    smallIndex = 0
    smallestRight = num[smallIndex]
    for i in range(1, len(num)):
        if num[i] <= smallestRight:
            smallestRight = num[i]
            smallIndex = i

    largeIndex = -1
    largeLeft = num[largeIndex]
    for i in range(len(num)-2, -1, -1):
        if num[i] >= largeLeft:
            largeLeft = num[i]
            largeIndex = i

    res = ""
    for i, v in enumerate(num):
        if i == largeIndex:
            res+=smallestRight
        elif i == smallIndex:
            res+=largeLeft
        else:
            res+=v


    return int(res)

    

#tests            
numbers = [1, 5, 10, 20]
print(solution(numbers))

numbers = [1, 3, 900, 10]
print(solution(numbers))

numbers = [1000, 10, 100]
print(solution(numbers))

numbers = [1, 2, 10, 7]
print(solution(numbers))

numbers = [1, 3, 3, 3]
print(solution(numbers))

numbers = [1000, 10, 9]
print(solution(numbers))

There are definitely far less complicated solutions.肯定有远没有那么复杂的解决方案。 For example, this is one I came up with (just taking a shot, I'm sure there's even more optimised solutions):例如,这是我想出的(只是试一试,我相信还有更优化的解决方案):

def flip(i):
    return int(''.join(str(i)[::-1]))


def strict_inc(xs, single_flip_allowed=True):
    for n, (a, b) in enumerate(zip(xs, xs[1:])):
        if a >= b:
            break
    else:
        return True
    # here, n will be the index at which the first problem occurs
    return single_flip_allowed and (
        (
            (n == 0 or xs[n-1] < flip(xs[n]))
            and (n == len(xs) or flip(xs[n]) < xs[n+1])
            and strict_inc(xs[n+1:], False)
        )
        or
        (
            (xs[n] < flip(xs[n+1]))
            and (n+1 == len(xs) or flip(xs[n+1]) < xs[n+2])
            and strict_inc(xs[n+2:], False)
        )
    )

(note that this implements your solution, but not the correct one, keep reading) (请注意,这实现了您的解决方案,但不是正确的,请继续阅读)

Although the function calls itself recursively, it never does so more than once, so there's no issues there.尽管 function 递归调用自身,但它不会多次调用,因此没有问题。

It basically works on the premise that you can only have a single flaw in the original series of positive integers.它基本上是在你只能在原始正整数系列中存在一个缺陷的前提下工作的。 So, it finds the first flaw, sees if it can be fixed by 'flipping' the first or second of integer in the flaw and it checks that the rest of the sequence in that case is still strictly increasing (without allow further flips).因此,它找到了第一个缺陷,查看是否可以通过“翻转”缺陷中的 integer 的第一个或第二个来修复它,并检查在这种情况下序列的 rest 是否仍然严格增加(不允许进一步翻转)。

You were asking for a review of your code, but given that it's clearly far from optimal, that would be quite an undertaking to do comprehensively.您要求审查您的代码,但鉴于它显然远非最佳,这将是一项相当全面的工作。

If you do want a review, I'd suggest trying https://codereview.stackexchange.com/ since StackOverflow is really more suited to asking for solutions to concrete technical problems.如果您确实想要评论,我建议您尝试https://codereview.stackexchange.com/因为 StackOverflow 确实更适合寻求具体技术问题的解决方案。

Turns out (thanks @barmar for pointing out my mistake, and to @kellybundy for pointing out OP's mistake) that a solution that does work isn't that complicated either (again with room for improvement):事实证明(感谢@barmar 指出我的错误,并感谢@kellybundy 指出 OP 的错误)一个可行的解决方案也没有那么复杂(同样还有改进的余地):

def check(xs):
    x = str(xs[1])
    return any(xs[0] < int(f'{x[0:i]}{x[i+1]}{x[i]}{x[i+2:]}') < xs[2]
               for i in range(len(x) - 1))


def strict_inc(xs, single_flip_allowed=True):
    for n, (a, b) in enumerate(zip(xs, xs[1:])):
        if a >= b:
            break
    else:
        return True
    # here, n will be the index at which the first problem occurs
    return single_flip_allowed and (
        (
            check(xs[n-1:n+2] if n > 0 else [-1] + xs[n:n+2])
            and strict_inc(xs[n+1:], False)
        )
        or
        (
            check(xs[n:n+3] if n < len(xs)-2 else xs[n:n+2] + [int('9'*len(str(xs[n+1])))])
            and strict_inc(xs[n+2:], False)
        )
    )

(meh, only flips consecutive digits - anyone who feels like fixing that issue is welcome to update the answer:)) (嗯,只翻转连续数字 - 欢迎任何想要解决该问题的人更新答案:))

After reading some of the input here and making some modifications I came up with this solution.在阅读了这里的一些输入并进行了一些修改后,我想出了这个解决方案。 Pretty similar to my initial solution but I think it is handling the cases the other was not.与我最初的解决方案非常相似,但我认为它正在处理另一个没有处理的情况。

def solution(numbers):
    flaw = False
    for i in range(len(numbers)-1):
        if numbers[i] >= numbers[i+1]:
            if flaw:
                return False
            else:
                flaw = True
                flawIndex = i

    if not flaw:
        return True

    a = swap(numbers[flawIndex])
    b = swap(numbers[flawIndex+1]) if flawIndex < len(numbers)-1 else None

    return (
        (
            True if (flawIndex != 0 or a > numbers[flawIndex]) and a < numbers[flawIndex+1] else False
        ) or (
            True if b < numbers[flawIndex] and (flawIndex < len(numbers)-1 or b < numbers[flawIndex+1]) else False
        )
    )

def swap(num):
    sn = str(num)
    l, r = max(sn), min(sn)
    largeIndex = sn.index(l)
    smallIndex = sn.rindex(r)
    res = ""
    for i in range(len(sn)):
        if i==smallIndex:
            res+=sn[largeIndex]
        elif i==largeIndex:
            res+=sn[smallIndex]
        else:
            res+=sn[i]

    return int(res)

I am at beginning of learning Scala, therefore, any suggestions of best practices are more than welcome.我刚开始学习 Scala,因此非常欢迎任何最佳实践建议。

def solution(numbers: Array[Int]) = {
    val swappedNumbers = numbers.map(listOfPermutation(_))
    val res = Array.fill(swappedNumbers.length)(-1)
    for {
      i <- 0 to res.length - 1
    } yield if (i == 0) res(i) = swappedNumbers(i)(i)
    else {
      res(i) = getNextNum(res(i - 1), swappedNumbers(i))
    }
    if (res.filter(_ == -1).size == 0) true else false
  }

  def getListOfDigits(int: Int) = {
    def getDigits(int: Int, acc: List[Int]): List[Int] = {
      if (int % 10 == 0 & int / 10 == 0) acc
      else {
        getDigits(int / 10, int % 10 :: acc)
      }
    }
    if (int > 0) getDigits(int, Nil) else int :: Nil
  }

  def listOfPermutation(int: Int): Array[Int] = {
    val arrOfDigits = getListOfDigits(int).toArray
    if (arrOfDigits.length <= 1) arrOfDigits
    else {
      val res = for {
        i <- (0 to arrOfDigits.length - 2).toArray
        j <- i + 1 to arrOfDigits.length - 1
      } yield {
        val swapped = (arrOfDigits(i), arrOfDigits(j)).swap
        val copyOfDig = arrOfDigits.clone()
        copyOfDig(i) = swapped._1
        copyOfDig(j) = swapped._2
        copyOfDig.mkString.toInt
      }
      res
        .filter(num => num <= int)
        .sortWith(_ < _) :+ arrOfDigits.mkString.toInt
    }
  }
  def getNextNum(num: Int, arrOfNum: Array[Int]): Int = {
    val res = arrOfNum.filter(_ > num)
    if (res.length > 0) res(0) else -1
  }

  println(solution(Array(13, 31, 30))) //false
  println(solution(Array(1, 5, 10, 20))) //true
  println(solution(Array(1, 3, 900, 10))) //true
  println(solution(Array(1000, 500, 400, 300))) //true

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM