Scala String Equality Question from Programming Interview

前端 未结 4 939
终归单人心
终归单人心 2021-02-12 19:52

Since I liked programming in Scala, for my Google interview, I asked them to give me a Scala / functional programming style question. The Scala functional style question that I

4条回答
  •  迷失自我
    2021-02-12 20:10

    You don't have to create the output to find the answer. You can iterate the two sequences at the same time and stop on the first difference. If you find no difference and both sequences terminate at the same time, they're equal, otherwise they're different.

    But now consider sequences such as this one: aaaa/// to compare with a. You need to consume 6 elements from the left sequence and one element from the right sequence before you can assert that they're equal. That means that you would need to keep at least 5 elements in memory until you can verify that they're all deleted. But what if you iterated elements from the end? You would then just need to count the number of backspaces and then just ignoring as many elements as necessary in the left sequence without requiring to keep them in memory since you know they won't be present in the final output. You can achieve O(1) memory using these two tips.

    I tried it and it seems to work:

    def areEqual(s1: String, s2: String) = {
        def charAt(s: String, index: Int) = if (index < 0) '#' else s(index)
    
        @tailrec
        def recSol(i1: Int, backspaces1: Int, i2: Int, backspaces2: Int): Boolean = (charAt(s1, i1), charAt(s2, i2)) match {
            case ('/',  _) => recSol(i1 - 1, backspaces1 + 1, i2, backspaces2)
            case (_,  '/') => recSol(i1, backspaces1, i2 - 1, backspaces2 + 1)
            case ('#' , '#') => true
            case (ch1, ch2)  => 
                if      (backspaces1 > 0) recSol(i1 - 1, backspaces1 - 1, i2    , backspaces2    )
                else if (backspaces2 > 0) recSol(i1    , backspaces1    , i2 - 1, backspaces2 - 1)
                else        ch1 == ch2 && recSol(i1 - 1, backspaces1    , i2 - 1, backspaces2    )
        }
        recSol(s1.length - 1, 0, s2.length - 1, 0)
    }
    

    Some tests (all pass, let me know if you have more edge cases in mind):

    // examples from the question
    val inputs = Array("abc", "aa/bc", "abb/c", "abcc/", "/abc", "//abc")
    for (i <- 0 until inputs.length; j <- 0 until inputs.length) {
        assert(areEqual(inputs(i), inputs(j)))
    }
    
    // more deletions than required
    assert(areEqual("a///////b/c/d/e/b/b", "b")) 
    assert(areEqual("aa/a/a//a//a///b", "b"))
    assert(areEqual("a/aa///a/b", "b"))
    
    // not enough deletions
    assert(!areEqual("aa/a/a//a//ab", "b")) 
    
    // too many deletions
    assert(!areEqual("a", "a/"))
    

    PS: just a few notes on the code itself:

    • Scala type inference is good enough so that you can drop types in the partial function inside your foldLeft
    • Nil is the idiomatic way to refer to the empty list case

    Bonus:

    I had something like Tim's soltion in mind before implementing my idea, but I started early with pattern matching on characters only and it didn't fit well because some cases require the number of backspaces. In the end, I think a neater way to write it is a mix of pattern matching and if conditions. Below is my longer original solution, the one I gave above was refactored laater:

    def areEqual(s1: String, s2: String) = {
        @tailrec
        def recSol(c1: Cursor, c2: Cursor): Boolean = (c1.char, c2.char) match {
            case ('/',  '/') => recSol(c1.next, c2.next)
            case ('/' ,   _) => recSol(c1.next, c2     )
            case (_   , '/') => recSol(c1     , c2.next)
            case ('#' , '#') => true
            case (a   ,   b) if (a == b) => recSol(c1.next, c2.next)
            case _           => false
        }
        recSol(Cursor(s1, s1.length - 1), Cursor(s2, s2.length - 1))
    }
    
    private case class Cursor(s: String, index: Int) {
        val char = if (index < 0) '#' else s(index)
        def next = {
          @tailrec
          def recSol(index: Int, backspaces: Int): Cursor = {
              if      (index < 0      ) Cursor(s, index)
              else if (s(index) == '/') recSol(index - 1, backspaces + 1)
              else if (backspaces  > 1) recSol(index - 1, backspaces - 1)
              else                      Cursor(s, index - 1)
          }
          recSol(index, 0)
        }
    }
    

提交回复
热议问题