3

I am wondering if there is a fancy way to find the index of, the first character in string a that does not match the character in the same position of string b, aside from using brute force.

Here is the brute force attempt:

bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
    if( a.Count() != b.Count() )
    {
        return false;
    }

    for(int index = 0; index < a.Count(); ++index)
    {
        if( a[index] != b[index] )
        {
            string validCharacters = " ^";

            if( !validCharacters.Contains(a[index]) ||
                !validCharacters.Contains(b[index]) )
            {
                return false;
            }
        }
    }

    return true;
}
Syntax Error
  • 1,600
  • 1
  • 26
  • 60
Christopher Pisz
  • 3,757
  • 4
  • 29
  • 65
  • Not an answer to your question, but I would use the `Length` property of string rather than `Count()` which which is a general-purpose linqism. Also your code would seem to imply a different question from what you actually asked. – zzxyz Mar 02 '18 at 00:59
  • Wait, you're just looking to see if two strings are the same minus a couple of characters? `return a.Replace(" ", "").Replace("^", "") == b.Replace(" ", "").Replace("^", "");` would seem to be the simplest way... – Heretic Monkey Mar 02 '18 at 01:04
  • @MikeMcCaughan - Differs in that yours says `"A^" == "^A"` whereas his does not. – zzxyz Mar 02 '18 at 01:12
  • 1
    @zzxyz Sure, but the name of the function is `OnlyDiffersByCarotsAndSpaces` so... ;) – Heretic Monkey Mar 02 '18 at 01:14
  • I'm pretty sure that walking the strings (what you're calling "brute force") is the only way to do this. What's wrong with what you have? – Rufus L Mar 02 '18 at 01:29
  • @zzxyz What do you mean *"his does not"*? Yes it does...doesn't it? He ignores all differences where one character is a space or a `^`. – Rufus L Mar 02 '18 at 01:30
  • @RufusL - On the first character, the left string char will be 'A', triggering `return false` – zzxyz Mar 02 '18 at 01:33
  • @zzxyz Oh, right. I need to go to sleep. thanks! – Rufus L Mar 02 '18 at 01:36
  • For such a short comparison, it's a little brain-melting. – zzxyz Mar 02 '18 at 01:38
  • 1
    I believe that this is most efficient way to do this (what you call “brute force”). – Joel Cornett Mar 02 '18 at 01:38
  • 1
    Is there a way to avoid the `validCharacters` lookup? Perhaps by converting `'^'` to `' '` before comparing? – mjwills Mar 02 '18 at 01:50
  • @mjwills *sigh* yes. I'm going to bed... – Rufus L Mar 02 '18 at 01:55

4 Answers4

1

I think you can use a combination of string.Split to break up your strings into arrays, split on the "valid characters", and then return the result of IEnumerable.SequenceEqual, which returns true if two IEnumerables contain the same elements in the same order:

private static bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
    // A few "exit quick" checks...
    if (a == null) return b == null;
    if (b == null) return false;

    var validChars = new[] {' ', '^'};
    var first = a.Split(validChars);
    var second = b.Split(validChars);

    return first.SequenceEqual(second);
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
0
int DifferenceIndex(string str1, string str2)
{
    int position = 0;
    int min = Math.Min(str1.Length, str2.Length);
    while (position < min && str1[position] == str2[position]) 
        position ++;

    return (position == min && str1.Length == str2.Length) ? -1 : position;
}

Returns -1 if the strings are the same.

Kronos24
  • 43
  • 7
Syntax Error
  • 1,600
  • 1
  • 26
  • 60
  • The OP has code in there that looks for spaces and carets; this code does not address that. – Heretic Monkey Mar 02 '18 at 01:15
  • 1
    Yeah but his question was `find the index of, the first character in string a that does not match the character in the same position of string b`, which the code I posted does. You should post your answer and see which one the guy picks! :) – Syntax Error Mar 02 '18 at 01:16
  • Yeah, it's a bit confusing considering his code returns a `bool`... I removed that part of my comment once I read that. – Heretic Monkey Mar 02 '18 at 01:17
0

Did somebody say fancy? :) This version will definitely perform worse than your version for high traffic and large strings, so depending on performance requirements you might not want to use it.

if (a.Length != b.Length) return -1;   
string allowed = " ^";
a = a.Replace(' ', '^');
b = b.Replace(' ', '^');
//at this point you can do string.Equals(a,b) and it will work correctly
var equalCharCount = a.Zip(b, (c1, c2) => c1 == c2).TakeWhile(c => c).Count();
//or even
//var idx = 0;
//var equalCharCont = a.TakeWhile(c => c == b[idx++]).Count() + 1;

A slightly fancier, worse performing, unfinished and most probably wrong idea would be:

var idx = a.Zip(b, (i1, i2) => Tuple.Create(i1, i2))
           .Select((value, index) => new { value, index })
           .SkipWhile(it => it.value.Item1 == it.value.Item2 || 
                                (allowed.Contains(it.value.Item1) && 
                                 allowed.Contains(it.value.Item2)))
           .Select(it => it.index)
           .First() + 1;
Alexandru Clonțea
  • 1,746
  • 13
  • 23
0

Well, that depends on what you mean by "fancy", and whether or not that's really what you want. For little utilities like this, it's often better to be as fast and make as little fuss as possible. Solutions involving IEnumerable will almost certainly be a bit more readable and "elegant", but under the covers, they'll accomplish the same thing, but likely with more overhead.

This method is slightly "brutier" and "forcier" than the original.

bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
  int len = a.Length; // eliminate Count() function call
  if (len != b.Length)
  {
    return false;
  }

  for (int index = 0; index < len; ++index)
  {
    if ((a[index] != b[index]) &&
        (((a[index] != ' ') && (a[index] != '^')) ||  // eliminate Contains() method calls
         ((b[index] != ' ') && (b[index] != '^'))))   //
    {
      return false;
    }
  }

  return true;
}
Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31