samedi 22 février 2020

How could we create an algorithm to check if a string is the result of two merged strings?

I am doing the following programming exercise: Merged String Checker

1) I have tried the following code:

import java.util.*;

public class StringMerger {
  public static boolean isMerge(String s, String part1, String part2) {
    System.out.println("\n\ns: "+s);
    System.out.println("part1: "+part1);
    System.out.println("part2: "+part2);
    if(!s.isEmpty() && part1.isEmpty() && part2.isEmpty()) return false;
    if( ( part1==null || part1.isEmpty() && part2.equals(s) ) || part2==null || part2.isEmpty() && part1.equals(s) ){
      return true;
    }

    /*Check if complete words from s are in part1 or part2*/

    List<String> sList = new ArrayList(Arrays.asList(s.split(" ")));
    List<String> part1List = new ArrayList(Arrays.asList(part1.split(" ")));
    List<String> part2List = new ArrayList(Arrays.asList(part2.split(" ")));
    System.out.println("sList: "+Arrays.toString(sList.toArray()));
    System.out.println("part1List: "+Arrays.toString(part1List.toArray()));
    System.out.println("part2List: "+Arrays.toString(part2List.toArray()));

    for(Iterator<String> it = sList.iterator(); it.hasNext(); ){
      String word = it.next();
      if(word.equals(part1List.get(0))){
        it.remove();
        part1List.remove(0);
        System.out.println("sList: "+Arrays.toString(sList.toArray()));
        System.out.println("part1List: "+Arrays.toString(part1List.toArray()));
      }else if(word.equals(part2List.get(0))){
        it.remove();
        part2List.remove(0);
        System.out.println("sList: "+Arrays.toString(sList.toArray()));
        System.out.println("part2List: "+Arrays.toString(part2List.toArray()));        
      }
    }

    s=String.join(" ",sList);
    part1=String.join(" ",part1List);
    part2=String.join(" ",part2List);
    System.out.println("\n\ns: "+s);
    System.out.println("part1: "+part1);
    System.out.println("part2: "+part2);

    /*Check if s first character is part1 or part2 initial character*/

    for(char letter : s.toCharArray()){
      System.out.println("letter: "+letter);
      System.out.println("part1: "+part1);
      System.out.println("part2: "+part2);

      if(!part1.isEmpty() && letter == part1.charAt(0)){
        part1 = part1.substring(1);
        System.out.println("part1: "+part1);
        s = s.substring(1);
      }else if(!part2.isEmpty() && letter==part2.charAt(0)){
        part2 = part2.substring(1);
        System.out.println("part2: "+part2);
        s = s.substring(1);
      }
      System.out.println("s: "+s);

      System.out.println("s.substring(0,part1.length()): "+s.substring(0,part1.length()));

      if(s.substring(0,part1.length()).equals(part1)){
        s=s.substring(part1.length());
        part1="";
        System.out.println("are equal, s: "+s);
      }else if(s.substring(0,part2.length()).equals(part2)){
        s=s.substring(part2.length());
        part2="";
        System.out.println("are equal, s: "+s);
      }

      if(s.isEmpty() || (part1.length()==0 && part2.length()==0) ) break;
    }
    System.out.println("\n\ns: "+s);
    System.out.println("part1: "+part1);
    System.out.println("part2: "+part2);
    return s.isEmpty() && part1.isEmpty() && part2.isEmpty();
  }
}

And I would like you to explain: why does it fail the following test‽

import org.junit.Test;
import static org.junit.Assert.*;

public class StringMergerTest {

  @Test
  public void suffledPartsLetters(){
    assertTrue("",StringMerger.isMerge("Can we merge it? Yes, we can!","nwe me?s, e cn","Ca erg it Yewa!"));
  }

}

I have identified in the trace where is behaves unexpectedly:

letter: **r**
part1: ?s, e cn
part2: e**r**g it Yewa!
s: rge it? Yes, we can!
s.substring(0,part1.length()): rge it? 

letter: **g**
part1: ?s, e cn
part2: er**g** it Yewa!
s: rge it? Yes, we can!
s.substring(0,part1.length()): rge it? 

I understand that letter r and g are not being detected because of the code just checks if it is the first character in part1 or part2.

However I do not fully understand how could we fix the previous code to let it handle this case, could you help me please?

Besides I have also researched and found this post which describes some exercises' javascript solutions:

CodeWars/ Merged String Checker

I tried to write the recursive one without looking at the solution, and I came up with:

public class StringMerger {
  public static boolean isMerge(String s, String part1, String part2) {
    System.out.println("\n\ns: "+s);
    System.out.println("part1: "+part1);
    System.out.println("part2: "+part2);

    if(s.length()!= (part1.length()+part2.length()) ){
      System.out.println("lengths are different");
      return false;
    }
    if(s.length()==0) {
      System.out.println("s.length is 0");
      return true;
    }
    if(part1.length()>0 && s.charAt(0)==part1.charAt(0)){
      System.out.println("s first char is part1 first char");
      isMerge(s.substring(1),part1.substring(1),part2);
    }
    if(part2.length()>0 && s.charAt(0)==part2.charAt(0)){
      System.out.println("s first char is part2 first char");
      isMerge(s.substring(1),part1,part2.substring(1));
    }
    return false;
  }
}

Why does the previous one fail the following tests?

import org.junit.Test;
import static org.junit.Assert.*;

public class StringMergerTest {

  @Test
  public void normalHappyFlow() {
    assertTrue("codewars can be created from code and wars", StringMerger.isMerge("codewars", "code", "wars"));
    assertTrue("codewars can be created from cdwr and oeas", StringMerger.isMerge("codewars", "cdwr", "oeas"));
    assertFalse("Codewars are not codwars", StringMerger.isMerge("codewars", "cod", "wars"));
  }

  @Test
  public void suffledPartsLetters(){
    assertTrue("",StringMerger.isMerge("Can we merge it? Yes, we can!","nwe me?s, e cn","Ca erg it Yewa!"));
  }

}

I expected that when all letters are matched with part1 or part2 letters, and s is empty with length 0, it would output true.

However it outputs false even when it detects s.length is 0.

The trace is:

s: codewars
part1: code
part2: wars
s first char is part1 first char


s: odewars
part1: ode
part2: wars
s first char is part1 first char


s: dewars
part1: de
part2: wars
s first char is part1 first char


s: ewars
part1: e
part2: wars
s first char is part1 first char


s: wars
part1: 
part2: wars
s first char is part2 first char


s: ars
part1: 
part2: ars
s first char is part2 first char


s: rs
part1: 
part2: rs
s first char is part2 first char


s: s
part1: 
part2: s
s first char is part2 first char


s: 
part1: 
part2: 
s.length is 0

How could we also fix the previous code? And why does it fails to pass the tests?

I have also read:

Best way to convert an ArrayList to a string ConcurrentModificationException for ArrayList java : remove words from ArrayList<String> Removing items from a list Converting array to list in Java Checking if a string is empty or null in Java

Aucun commentaire:

Enregistrer un commentaire