ArchAngel
ArchAngel

Reputation: 644

Merged String Checker (custom rules)

Write an algorithm to check if a given string, s, can be formed from two other strings, part1 and part2.

The restriction is that the characters in part1 and part2 are in the same order as in s.

Example:

'codewars' is a merge from 'cdw' and 'oears':

s:  c o d e w a r s   = codewars
part1:  c   d   w         = cdw
part2:    o   e   a r s   = oears

But in the test cases, it's not so easy to make it work..

The rules says they have to be the same length and in same order but with random test cases they are put in random order aswell.. So the rules conflict with the rules so how can I make it work??

Test cases that follow the rules:

[Test]
public void SadPath2()
{
    Assert.IsFalse(StringMerger.isMerge("codewars", "code", "wasr"), "Codewars can't be created from code and wasr");
}
SadPath2 == false;

[Test]
public void SadPath1()
{
    Assert.IsFalse(StringMerger.isMerge("codewars", "cod", "wars"), "Codewars are not codwars");
}
SadPath1 == false;

[Test]
public void HappyPath2()
{
     Assert.IsTrue(StringMerger.isMerge("codewars", "cdwr", "oeas"), "codewars can be created from cdwr and oeas");
}
HappyPath2 == true;

[Test]
public void HappyPath1()
{
     Assert.IsTrue(StringMerger.isMerge("codewars", "code", "wars"), "codewars can be created from code and wars");
}
HappyPath1 == true;

Some random tests that might not follow the rules completely:

[Test]
public void RandomTest()
{
    Assert.IsTrue(StringMerger.isMerge("[W`meSnw(R1qaLLqc[=]=UAvTa_3%", "W`mnwqaLL]=va%", "[eS(R1qc[=UAT_3"), "'[W`meSnw(R1qaLLqc[=]=UAvTa_3%' is a merge of 'W`mnwqaLL]=va%' and '[eS(R1qc[=UAT_3'");
}    
RandomTest == true;

[Test]
public void RandomTest2()
{
    Assert.IsTrue(StringMerger.isMerge("]ftUNn7-XoX4AZ3i1+", "U7oX4A1+", "]ftNn-XZ3i"), "']ftUNn7-XoX4AZ3i1+' is a merge of 'U7oX4A1+' and ']ftNn-XZ3i");
}
RandomTest2 == true;

First example on my class where it can successfully run the all the tests Except SadPath2:

public class StringMerger
{
    public static bool isMerge(string s, string part1, string part2)
    {
        int num = 0;
        string txt1 = "";
        Console.WriteLine("S - " + s + " - P1 - " + part1 + " - P2 - " + part2);

        #region Sorting
        foreach (var itm in s)
        {
            if (part1.Contains(itm.ToString()))
                num++;
            else if (part2.Contains(itm.ToString()))
                num++;
        }
        #endregion

        if (num == s.Length && num == (part1.Length + part2.Length))
                return true;


        return false;
    }
}

enter image description here

Second example where it can run all except for the random tests:

public class StringMerger
{
    public static bool isMerge(string s, string part1, string part2)
    {
        int num = 0;
        int P1 = 0;
        int P2 = 0;
        bool p2 = false;
        string txt1 = "";

        #region Sorting
        foreach (var itm in s)
        {
            if (part1.Contains(itm.ToString()))
                num++;
            else if (part2.Contains(itm.ToString()))
                num++;

            try
            {
                if (p2 == false)
                {
                    txt1 = txt1 + GetLetterP1(part1, itm.ToString(), P1);
                    P1++;
                    p2 = true;
                }
                else
                {
                    txt1 = txt1 + GetLetterP2(part2, itm.ToString(), P2);
                    P2++;
                    p2 = false;
                }
            }
            catch { }
        }
        #endregion

        if (num == s.Length && num == (part1.Length + part2.Length))
        {
            if (s.Contains(part1 + part2))
                return true;
            else if (txt1 == s)
                return true;
        }


        return false;
    }

    static string GetLetterP1(string p1, string letter, int n)
    {
        string itm = "";
        if (p1.Contains(letter))
            itm = p1[n].ToString();
        return itm;
    }

    static string GetLetterP2(string p2, string letter, int n)
    {
        string itm = "";
        if (p2.Contains(letter))
            itm = p2[n].ToString();
        return itm;
    }
}

enter image description here

Results with Kvam answer: (With the latest fixes)

enter image description here

Results with Max Answer: (With the latest fixes)

enter image description here

Results with codersl Answer: (With the latest fixes)

enter image description here

Upvotes: 0

Views: 861

Answers (3)

codersl
codersl

Reputation: 2332

My 2 cents

public static void Main()
{
    Console.WriteLine(Check("codewars", "code", "wasr")); //false
    Console.WriteLine(Check("codewars", "cod", "wars")); //false
    Console.WriteLine(Check("codewars", "cdwr", "oeas"));
    Console.WriteLine(Check("codewars", "code", "wars"));
    Console.WriteLine(Check("[W`meSnw(R1qaLLqc[=]=UAvTa_3%", "W`mnwqaLL]=va%", "[eS(R1qc[=UAT_3"));
    Console.WriteLine(Check("]ftUNn7-XoX4AZ3i1+", "U7oX4A1+", "]ftNn-XZ3i"));
    Console.WriteLine(Check("acab", "ab", "ac"));
    Console.WriteLine(Check("b]aDw- !oKJnOJ", "b]aDwoKJ", "- !nOJ"));
    Console.WriteLine(Check("codewars", "codewarss", "")); //false
    Console.WriteLine(Check("codewars", "", "")); //false
    Console.WriteLine(Check("codewars", "codewars", null));
    Console.WriteLine(Check("Bananas from Bahamas", "Bahas", "Bananas from am"));
}

private static bool Check(string s, string part1, string part2)
{
    if (part1 == null)
        part1 = "";

    if (part2 == null)
        part2 = "";

    var part1Index = 0;
    var part2Index = 0;
    var bothMatch = "";

    foreach(var c in s)
    {
        // handle both strings matching
        if (part1Index < part1.Length && part2Index < part2.Length && c == part1[part1Index] && c == part2[part2Index])
        {
            bothMatch += c;
            part1Index++;
            part2Index++;
            continue;
        }

        if (bothMatch.Length > 0 && c == part1[part1Index])
        {
            // part2 doesn't match anymore so roll back its index
            part2Index -= bothMatch.Length;
            bothMatch = "";
        }
        else if (bothMatch.Length > 0 && c == part2[part2Index])
        {
            // part1 doesn't match anymore so roll back its index
            part1Index -= bothMatch.Length;
            bothMatch = "";
        }

        // handle one string matching
        if (part1Index < part1.Length && c == part1[part1Index])
        {
            //Console.WriteLine("c={0}, p1={1}", c, part1[part1Index]);
            part1Index++;
            continue;
        }
        if (part2Index < part2.Length && c == part2[part2Index])
        {
            //Console.WriteLine("c={0}, p2={1}", c, part2[part2Index]);
            part2Index++;
            continue;
        }

        //Console.WriteLine("c={0}, p1={1}, p2={2}, both={3}", c, part1[part1Index], part2[part2Index], bothMatch);
        return false;
    }
    return (part1Index == part1.Length) && (part2Index == part2.Length);
}

Upvotes: 0

Kvam
Kvam

Reputation: 2218

I think this should cover the requirements, although you might want to rewrite it to avoid string creation all around.

public bool IsMatch(string target, string part1, string part2)
{
    if (target.Length != part1.Length + part2.Length)
    {
        return false;
    }
    if (target == "")
    {
        return true;
    }

    if (part1.Length > 0 && target[0] == part1[0])
    {
        if (IsMatch(target.Substring(1), part1.Substring(1), part2.Substring(0)))
        {
            return true;
        }
    }
    if (part2.Length > 0 && target[0] == part2[0])
    {
        if (IsMatch(target.Substring(1), part1.Substring(0), part2.Substring(1)))
        {
            return true;
        }
    }

    return false;
}

Upvotes: 2

Max
Max

Reputation: 7100

Here my 2 cents

public bool IsMerge(string target, string part1, string part2)
{
    if (String.IsNullOrEmpty(target))
    {
        throw new ArgumentNullException("target");
    }

    if (String.IsNullOrEmpty(part1))
    {
        throw new ArgumentNullException("part1");
    }

    if (String.IsNullOrEmpty(part2))
    {
        throw new ArgumentNullException("part2");
    }

    if (target.Length == (part1.Length + part2.Length))
    {
        return this.IsPart(target, part1) && this.IsPart(target, part2);
    }

    return false;
}

private bool IsPart(string target, string part)
{
    int idx = -1;
    int lastIdx = 0;

    for (int i = 0; i < part.Length; i++)
    {
        idx = (target.IndexOf(part[i], lastIdx));
        if (!(idx > -1))
        {
            return false;
        }

        lastIdx = idx;
    }

    return true;
}

Upvotes: 0

Related Questions