Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Backspace String Comparisons: Two Ways To Approach a Common Algorithm
ab
ab

Posted on • Edited on

     

Backspace String Comparisons: Two Ways To Approach a Common Algorithm

Suppose you're given two strings, "ab#c" and "ad#c". The "#" key is a backspace character, which means it deletes the previous character in the string. Return whether or not the two strings are equal. (The Leetcode problem can be foundhere.)

For this algorithm, there are two common approaches: using and manipulating arrays, and one that involves a two pointer solution. In this post, I'll walk through both approaches.

Using Arrays

The idea behind this approach is to initialize two empty arrays (one for each inputted string). Then, walk through each inputted string, checking each character. If the character isnot "#", then add the character to the array. If it is "#", then pop the last element off of the array. Then, join both of the arrays (turning them into strings), and compare if those strings are equal.

First, we'll initialize the arrays, and write out the return statement. I like to include a return statement when I'm beginning to write out a function so that I always have the purpose of the function in mind while I'm working.

functionbackspaceCompare(S,T){letsArr=[];lettArr=[];//...returnsArr===tArr;}
Enter fullscreen modeExit fullscreen mode

Now, we'll create two different for loops, one for each inputted string. We're checking these strings separately, and then we will be modifying their corresponding arrays.

functionbackspaceCompare(S,T){letsArr=[];lettArr=[];for(leti=0;i<S.length;i++){//...}for(leti=0;i<T.length;i++){//...}//...returnsArr===tArr;}
Enter fullscreen modeExit fullscreen mode

Inside the first for loop, we will check if the character we're currently on in the string is "#". If it's not, then we will add that character to the string using.push(). If it is, then we will simply pop off the last element from the sArr array. We can do the same in the second loop with the T input and the tArr.

functionbackspaceCompare(S,T){letsArr=[];lettArr=[];for(leti=0;i<S.length;i++){if(S[i]==="#"){sArr.pop();}else{sArr.push(S[i]);}}for(leti=0;i<T.length;i++){if(T[i]==="#"){tArr.pop();}else{tArr.push(T[i]);}}//...returnsArr===tArr;}
Enter fullscreen modeExit fullscreen mode

Finally, we will turn the arrays into strings by using .join(""). We do this in order to check if they're equal to each other. In JavaScript,[1, 2, 3] === [1, 2, 3] will return false, but123 === 123 will return true. The reason behind this is that in JS, the=== operator checks if the arrays have the same object reference. Every time an object is created, it points to a different spot in memory, so even if its contents are identical, the spot in memory is different.

functionbackspaceCompare(S,T){letsArr=[];lettArr=[];for(leti=0;i<S.length;i++){if(S[i]==="#"){sArr.pop();}else{sArr.push(S[i]);}}for(leti=0;i<T.length;i++){if(T[i]==="#"){tArr.pop();}else{tArr.push(T[i]);}}sArr=sArr.join("");tArr=tArr.join("");returnsArr===tArr;}
Enter fullscreen modeExit fullscreen mode

This array-based approach has O(n) space complexity and O(n) time complexity.

Two Pointers

The two pointers approach involves walking through both inputted strings. If either string's element is a "#", then we will "skip over" the next element in that string once we get to it. If neither string's element is a "#", then we will check if the strings are equal at those points. If they're not equal, then we can return false. If they are equal, we can continue moving down the string, until there are no more characters in either input to check.

This approach took a little longer for me to grasp, so once I go through the code, I'll use and example and walk through each line of code to show how we reach its output.

The Code

First, we'll want to start at the end of both strings, so we should initialize variables at the length of both strings minus 1 (because they start at the 0 index). We also want to initialize skip counts for each inputted string. Skip counts will enable us to keep track of if we've just seen a "#", and if so, to skip over the next element.

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;//...
Enter fullscreen modeExit fullscreen mode

Now, we'll want to initiate a while loop. As long as there are elements left in either string to check, we should keep checking them, so we should do a while loop that continues so long as i OR j are greater than or equal to 0. I'll also use this moment to add areturn true line at the end, because inside of my while loop, I'll be checking to see if the characters of each stringdon't equal each other, which means that if they pass all of the checks in the loop, then they must equal each other.

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;while(i>=0||j>=0){//...}returntrue;}
Enter fullscreen modeExit fullscreen mode

Now, we can do the first checks inside of the while loop. The first thing we want to check for is if the current element in the first string equals "#". If it does, then we want to add 1 to our skip count (which we'll later use), and also to decrease the pointer by 1 (aka to get closer to the start of the string).

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;while(i>=0||j>=0){if(S[i]==="#"){sSkipCount++;i--;}//...}returntrue;}
Enter fullscreen modeExit fullscreen mode

The next check is to see if the skip count for the first string is greater than 0--as in, we just saw a "#", so this element will be deleted. If the skip count for the S checker is greater than 0, and we're not yet finished checking the S string, then we can decrement the skip count, and also decrement i. Decrementing the skip count basically means we're passing over this element, but the next element still should be checked.

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;while(i>=0||j>=0){if(S[i]==="#"){sSkipCount++;i--;}elseif(sSkipCount>0&&i>=0){sSkipCount--;i--;}//...}returntrue;}
Enter fullscreen modeExit fullscreen mode

Now, the following two checks are essentially the same, but for the T input.

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;while(i>=0||j>=0){if(S[i]==="#"){sSkipCount++;i--;}elseif(sSkipCount>0&&i>=0){sSkipCount--;i--;}elseif(T[j]==="#"){tSkipCount++;j--;}elseif(tSkipCount>0&&j>=0){tSkipCount--;j--;}//...}returntrue;}
Enter fullscreen modeExit fullscreen mode

At this point, if the while loop has gone through all of these if and else-if statements, that means there's still elements to check, the current element in both strings is not "#", and there are no elements to skip over. Now, we can check if the strings at both of the counters are equal to each other. If they are not, then we can return false. Otherwise, we can simply decrement both i and j.

functionbackspaceCompare(S,T){leti=S.length-1;letj=T.length-1;letsSkipCount=0;lettSkipCount=0;while(i>=0||j>=0){if(S[i]==="#"){sSkipCount++;i--;}elseif(sSkipCount>0&&i>=0){sSkipCount--;i--;}elseif(T[j]==="#"){tSkipCount++;j--;}elseif(tSkipCount>0&&j>=0){tSkipCount--;j--;}elseif(S[i]!==T[j]){returnfalse;}else{i--;j--;}}returntrue;}
Enter fullscreen modeExit fullscreen mode

An Example

Now that we have the full written out code for this solution (which has O(n) time and O(1) space), it would be helpful to walk through this code using an example.

Let's sayS = "ab#c" andT = "ad#c". We start out with i, j, sSkipCount, and tSkipCount.

Truth table with i = 3, sSkipCount = 0, j = 3, tSkipCount = 0

Since i >= 0 or j >= 0, we'll enter the while loop. None of the if or else if statements are true, so we end up onelse { i--; j-- }.

Truth table with i = 2, sSkipCount = 0, j = 2, sSkipCount = 0

The while loop is still true, so we enter it again. S[i] === "#", so we will increment the skip count, and decrement i.

Truth table with i = 1, sSkipCount = 1

The while loop is still true. sSkipCount is greater than 0, and i >= 0, so we'll decrement the skip count, and decrement i.

Truth table with i = 0, sSkipCount = 0

The while loop is still true. T[j] === "#", so we'll increment the skip count, and decrement j.

Truth table with j = 1, tSkipCount = 1

The while loop is still true. tSkipCount is greater than 0, and j >= 0, so we'll decrement the skip count, and decrement j.

Truth table with j = 0, tSkipCount = 0

The while loop is still true. None of the if or else if statements apply, so again we end up atelse { i--; j-- }.

Truth table with i = -1, sSkipCount = 0, j = -1, tSkipCount = 0

The while loop is not true, so we do not enter it. Now, we can just return true.


Please let me know in the comments if you have any questions or alternate solutions!

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
chety profile image
Chety
  • Joined
• Edited on• Edited

Hi Alisa,

Great series by the way thanks. Here is my solution. Maybe it helps someone else

functionpureWord(word){returnword.split('').reduce((pure,char,i)=>{if(char==="#"){pure=word.slice(i+1)}returnpure;},word)}varbackspaceCompare=function(s,t){returnpureWord(s)===pureWord(t);};
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
khuongduybui profile image
Duy K. Bui
  • Joined

Isn't the point of having 2 pointers is to have them move together?

I think if you rewrite your if / else chain a little bit and break it out into 2 separate if blocks, then you can always decrease i and j together (at least until one of them reaches 0).

CollapseExpand
 
andreasanta profile image
Andrea S.

I agree the idea of starting from the end simplifies things, but the if blocks should be broken into three groups. Sometimes I even use a while loop per string to skip all consecutive occurrences of #.

CollapseExpand
 
veganhunter profile image
vegan-hunter
  • Joined

C#

    internal class BComparer    {        public static bool Compare(string a, string b)        {            if (a == null && b == null) return true;            if (a == null || b == null) return false;            var ia = a.Length - 1;            var ib = b.Length - 1;            while (true)            {                var sa = 0;                var sb = 0;                while (ia >= 0 && a[ia] == '#') { ia--; sa++; }                while (ib >= 0 && b[ib] == '#') { ib--; sb++; }                ia -= sa;                ib -= sb;                if (ia < 0 && ib < 0) return true;                if (ia < 0 || ib < 0) return false;                if (a[ia] != b[ib]) return false;                ia--;                ib--;            }        }    }
Enter fullscreen modeExit fullscreen mode

XUnit

    public class UnitTest1    {        [Theory]        [InlineData("ab#c", "ad#c", true)]        [InlineData("#c", "#a", false)]        [InlineData("##a", "#a", true)]        [InlineData("aa##c", "x#c", true)]        [InlineData("abc", "abx#c", true)]        [InlineData("ab", "abx#", true)]        [InlineData("abc", "abcxx##", true)]        [InlineData("a#bc", "bcxx##", true)]        [InlineData("bbc", "abcxx##", false)]        [InlineData("abb", "abcxx##", false)]        [InlineData("##a###", "", true)]        [InlineData("", "a#", true)]        [InlineData("", "#", true)]        [InlineData("", "", true)]        [InlineData(null, "", false)]        [InlineData(null, null, true)]        public void Test1(string a, string b, bool expected)        {            var result = BComparer.Compare(a, b);            Assert.True(expected == result, $"Incorrect result for: '{a}' and '{b}'");        }         }
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
yogeshgalav7 profile image
Yogesh Galav
Creating better product for better society.

When will the condition
(sSkipCount > 0 && i >= 0) and(tSkipCount > 0 && j >= 0)
will execute? isn't that is dead code because while loop terminates on same condition?
while (i >= 0 || j >= 0)

CollapseExpand
 
thisdotmedia_staff profile image
This Dot Media
This Dot provides teams with technical leaders who bring deep knowledge of the web platform. We help teams set new standards, and deliver results predictably.
  • Location
    Online
  • Joined

Really helpful! Thanks Alisa 😊

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Joined

More fromab

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp