14

I was recently asked this question in an interview:

Given two strings s and t, return if they are equal when both are typed into empty text editors. # means a backspace character.

Input: S = "ab#c", T = "ad#c"
Output: true
Explanation: Both S and T become "ac".

I came up with below solution but it is not space efficient:

  public static boolean sol(String s, String t) {
    return helper(s).equals(helper(t));
  }

  public static String helper(String s) {
    Stack<Character> stack = new Stack<>();
    for (char c : s.toCharArray()) {
      if (c != '#')
        stack.push(c);
      else if (!stack.empty())
        stack.pop();
    }
    return String.valueOf(stack);
  }

I wanted to see if there is any better way to solve this problem which doesn't use stack. I mean can we solve it in O(1) space complexity?

Note: we could have multiple backspace characters in as well.

flash
  • 1,455
  • 11
  • 61
  • 132
  • Are you looking for # or a backspace character? – Zephyr Jun 15 '19 at 23:47
  • Look at my example in the above question, you will understand. It's a backspace character. – flash Jun 15 '19 at 23:47
  • I imagine a regex could remove any character immediately preceding the # – Zephyr Jun 15 '19 at 23:49
  • We need to implement this in traditional way instead of using regex I think. – flash Jun 15 '19 at 23:49
  • In that case, loop through the chars in your String and when you encounter a #, move your index back and skip 2 characters? – Zephyr Jun 15 '19 at 23:50
  • 2
    You don't need any extra storage at all to do this. Just the two original strings. – user207421 Jun 16 '19 at 00:27
  • I think it is possible, but it of want `O(1)` extra storage, you will need `O(N^2)` time. Computing the `i`th character with no extra storage is `O(N)`. – Stephen C Jun 16 '19 at 02:16
  • @StephenC why O(n^2)? Aren't the two presented answers linear time? – גלעד ברקן Jun 16 '19 at 09:28
  • 1
    Possible duplicate of [Scala String Equality Question from Programming Interview](https://stackoverflow.com/questions/52241686/scala-string-equality-question-from-programming-interview) – wchargin Jun 17 '19 at 15:00
  • @wchargin I am not sure that this is duplicate, because the question is specific to `java`, not `scala`. – Oleksandr Pyrohov Jun 17 '19 at 19:14
  • @Oleksandr: Both questions are about the algorithm (improving the space complexity). The implementation language is not very relevant; answers from either could easily be translated into the other. – wchargin Jun 17 '19 at 20:05

2 Answers2

14

In order to achieve O(1) space complexity, use Two Pointers and start from the end of the string:

public static boolean sol(String s, String t) {
    int i = s.length() - 1;
    int j = t.length() - 1;
    while (i >= 0 || j >= 0) {
        i = consume(s, i);
        j = consume(t, j);
        if (i >= 0 && j >= 0 && s.charAt(i) == t.charAt(j)) {
            i--;
            j--;
        } else {
            return i == -1 && j == -1;
        }
    }
    return true;
}

The main idea is to maintain the # counter: increment cnt if character is #, otherwise decrement it. And if cnt > 0 and s.charAt(pos) != '#' - skip the character (decrement position):

private static int consume(String s, int pos) {
    int cnt = 0;
    while (pos >= 0 && (s.charAt(pos) == '#' || cnt > 0)) {
        cnt += (s.charAt(pos) == '#') ? +1 : -1;
        pos--;
    }
    return pos;
}

Time complexity: O(n).

Source 1, Source 2.

Oleksandr Pyrohov
  • 14,685
  • 6
  • 61
  • 90
  • 4
    Do you know the time complexity of this? My hunch is that while there are two loops now, it is still bounded by the number of backspaces, which in turn is bounded by the combined length of the strings (i.e. linear). – Thilo Jun 16 '19 at 02:48
  • 2
    @Thilo You are right! The time complexity is `O(n)`: the outer `while` loop just passes the responsibility to the inner loop from time to time. – Oleksandr Pyrohov Jun 16 '19 at 09:36
  • sol("", "a") will return true in this code. Perhaps ok for an interview. Not ok for a correct solution. – No Refunds No Returns Oct 29 '21 at 00:19
2

Corrected pseudocode of templatetypedef

// Index of next spot to read from each string
let sIndex = s.length() - 1
let tIndex = t.length() - 1
let sSkip = 0
let tSkip = 0

while sIndex >= 0 and tIndex >= 0:
    if s[sIndex] = #:
        sIndex = sIndex - 1
        sSkip = sSkip + 1
        continue
    else if sSkip > 0
        sIndex = sIndex - 1
        sSkip = sSkip - 1
        continue

    // Do the same thing for t.
    if t[tIndex] = #:
        tIndex = tIndex - 1
        tSkip = tSkip + 1
        continue
    else if tSkip > 0
        tIndex = tIndex - 1
        tSkip = tSkip - 1
        continue

    // Compare characters.
    if s[sIndex] != t[tIndex], return false

    // Back up to the next character
    sIndex = sIndex - 1
    tIndex = tIndex - 1

// The strings match if we’ve exhausted all characters.
return sIndex < 0 and tIndex < 0
ciamej
  • 6,918
  • 2
  • 29
  • 39