Alex67
Alex67

Reputation: 23

Neat solution to a counting within a string

I am trying to solve the following problem but cannot find an elegant solution. Any ideas? Thanks.

Input - a variable length string of numbers, e.g., string str = "5557476374202110373551116201";

Task - Check (from left to right) that each number (ignoring the repetitions) does not appear in the following 2 indexes. Using eg. above, First number = 5. Ignoring reps we see that last index of 5 in the group is 2. So we check next 2 indexes, i.e. 3 and 4 should not have 5. If it does we count it as error. Goal is to count such errors in the string.

In the above string errors are at indexes, 3,10 and 16.

Upvotes: 1

Views: 519

Answers (4)

jspcal
jspcal

Reputation: 51894

in addition to the other excellent solutions you can use a simple regexp:

foreach (Match m in Regexp.Matches(str, @"(\d)(?!\1)(?=\d\1)"))
    Console.WriteLine("Error: " + m.Index);

returns 3,10,16. this would match adjacent errors using lookahead with a backreference. handles repetitions. .net should support that. if not, you can use a non-backreference version:

(?<=0[^0])0|(?<=1[^1])1|(?<=2[^2])2|(?<=3[^3])3|(?<=4[^4])4|(?<=5[^5])5|(?<=6[^6])6|(?<=7[^7])7|(?<=8[^8])8|(?<=9[^9])9

Upvotes: 5

Dan Tao
Dan Tao

Reputation: 128317

Here's something I threw together in C# that worked with the example input from the question. I haven't checked it that thoroughly, though...

public static IEnumerable<int> GetErrorIndices(string text) {
    if (string.IsNullOrEmpty(text))
        yield break;

    int i = 0;
    while (i < text.Length) {
        char c = text[i];

        // get the index of the next character that isn't a repetition
        int nextIndex = i + 1;
        while (nextIndex < text.Length && text[nextIndex] == c)
            nextIndex++;

        // if we've reached the end of the string, there's no error
        if (nextIndex + 1 >= text.Length)
            break;

        // we actually only care about text[nextIndex + 1],
        // NOT text[nextIndex] ... why? because text[nextIndex]
        // CAN'T be a repetition (we already skipped to the first
        // non-repetition)
        if (text[nextIndex + 1] == c)
            yield return i;

        i = nextIndex;
    }

    yield break;
}

Upvotes: 1

Mike Tunnicliffe
Mike Tunnicliffe

Reputation: 10762

Sorry, not a C# man, but here's a simple solution in Ruby:

a="5557476374202110373551116201"
0.upto(a.length) do |i|
  puts "error at #{i}" if a[i]!=a[i+1] && a[i]==a[i+2]
end

Output:

error at 3
error at 10
error at 16

Upvotes: 2

LBushkin
LBushkin

Reputation: 131646

A simple indexed for loop with a couple of look ahead if checks would work. You can treat a string as a char[] or as an IEnumerable - either way you can use that to loop over all of the characters and perform a lookahead check to see if the following one or two characters is a duplicate.

Upvotes: 3

Related Questions