Scala String Equality Question from Programming Interview

前端 未结 4 860
终归单人心
终归单人心 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:01

    This code takes O(N) time and needs only three integers of extra space:

    def solution(a: String, b: String): Boolean = {
    
      def findNext(str: String, pos: Int): Int = {
        @annotation.tailrec
        def rec(pos: Int, backspaces: Int): Int = {
          if (pos == 0) -1
          else {
            val c = str(pos - 1)
            if (c == '/') rec(pos - 1, backspaces + 1)
            else if (backspaces > 0) rec(pos - 1, backspaces - 1)
            else pos - 1
          }
        }
        rec(pos, 0)
      }
    
      @annotation.tailrec 
      def rec(aPos: Int, bPos: Int): Boolean = {
        val ap = findNext(a, aPos)
        val bp = findNext(b, bPos)
        (ap < 0 && bp < 0) ||
        (ap >= 0 && bp >= 0 && (a(ap) == b(bp)) && rec(ap, bp))
      }
    
      rec(a.size, b.size)
    }
    

    The problem can be solved in linear time with constant extra space: if you scan from right to left, then you can be sure that the /-symbols to the left of the current position cannot influence the already processed symbols (to the right of the current position) in any way, so there is no need to store them. At every point, you need to know only two things:

    1. Where are you in the string?
    2. How many symbols do you have to throw away because of the backspaces

    That makes two integers for storing the positions, and one additional integer for temporary storing the number of accumulated backspaces during the findNext invocation. That's a total of three integers of space overhead.

    Intuition

    Here is my attempt to formulate why the right-to-left scan gives you a O(1) algorithm:

    The future cannot influence the past, therefore there is no need to remember the future.

    The "natural time" in this problem flows from left to right. Therefore, if you scan from right to left, you are moving "from the future into the past", and therefore you don't need to remember the characters to the right of your current position.

    Tests

    Here is a randomized test, which makes me pretty sure that the solution is actually correct:

    val rng = new util.Random(0)
    def insertBackspaces(s: String): String = {
      val n = s.size
      val insPos = rng.nextInt(n)
      val (pref, suff) = s.splitAt(insPos)
      val c = ('a' + rng.nextInt(26)).toChar
      pref + c + "/" + suff
    }
    
    def prependBackspaces(s: String): String = {
      "/" * rng.nextInt(4) + s
    }
    
    def addBackspaces(s: String): String = {
      var res = s
      for (i <- 0 until 8) 
        res = insertBackspaces(res)
      prependBackspaces(res)
    }
    
    for (i <- 1 until 1000) {
      val s = "hello, world"
      val t = "another string"
    
      val s1 = addBackspaces(s)
      val s2 = addBackspaces(s)
      val t1 = addBackspaces(t)
      val t2 = addBackspaces(t)
    
      assert(solution(s1, s2))
      assert(solution(t1, t2))
      assert(!solution(s1, t1))
      assert(!solution(s1, t2))
      assert(!solution(s2, t1))
      assert(!solution(s2, t2))
    
      if (i % 100 == 0) {
        println(s"Examples:\n$s1\n$s2\n$t1\n$t2")
      }
    }
    

    A few examples that the test generates:

    Examples:
    /helly/t/oj/m/, wd/oi/g/x/rld
    ///e/helx/lc/rg//f/o, wosq//rld
    /anotl/p/hhm//ere/t/ strih/nc/g
    anotx/hb/er sw/p/tw/l/rip/j/ng
    Examples:
    //o/a/hellom/, i/wh/oe/q/b/rld
    ///hpj//est//ldb//y/lok/, world
    ///q/gd/h//anothi/k/eq/rk/ string
    ///ac/notherli// stri/ig//ina/n/g
    Examples:
    //hnn//ello, t/wl/oxnh///o/rld
    //helfo//u/le/o, wna//ova//rld
    //anolq/l//twl//her n/strinhx//g
    /anol/tj/hq/er swi//trrq//d/ing
    Examples:
    //hy/epe//lx/lo, wr/v/t/orlc/d
    f/hk/elv/jj//lz/o,wr// world
    /anoto/ho/mfh///eg/r strinbm//g
    ///ap/b/notk/l/her sm/tq/w/rio/ng
    Examples:
    ///hsm/y//eu/llof/n/, worlq/j/d
    ///gx//helf/i/lo, wt/g/orn/lq/d
    ///az/e/notm/hkh//er sm/tb/rio/ng
    //b/aen//nother v/sthg/m//riv/ng
    

    Seems to work just fine. So, I'd say that the Google-guy did not mess up, looks like a perfectly valid question.

提交回复
热议问题