Search code examples
javafor-loopwhile-loopsubstringindexoutofboundsexception

Print all the combination of substrings from a given string in order by letter


This question has been asked and answered plenty of times before. However, I'm specifically asking for the substrings to be printed in the specific order by each letter as shown in the output.

import java.util.*;

public static void main(String[] args)
{
    breakWordIntoPieces("ABIGWORD");    
}  

public static void breakWordIntoPieces(String str)
{
    if (str.length() > 1) // I'm skipping all substrings of less than 2 letters
    {

        List<String> fragments = breakWordIntoPiecesFromRightToLeft(str);

        for (String fragment : fragments) // I'm using the variable name "fragments" as 
                                          // another word for "substring".
        {
            System.out.print("\n" + fragment);
        }
    }
    System.out.print("\n");

    str = removeFirstChar(str, 1);
    List<String> fragments2 = breakWordIntoPiecesFromRightToLeft(str);

    for (String fragment : fragments2)
    {
        System.out.print("\n" + fragment);
    }

    if (str.length() > 1)
    {
        str = removeFirstChar(str, 1);
        List<String> fragments3 = breakWordIntoPiecesFromRightToLeft(str);

        System.out.print("\n");

        for (String fragment : fragments3)
        {
            System.out.print("\n" + fragment);
        }
    }

    if (str.length() > 1)
    {
        str = removeFirstChar(str, 1);
        List<String> fragments4 = breakWordIntoPiecesFromRightToLeft(str);

        System.out.print("\n");

        for (String fragment : fragments4)
        {
            System.out.print("\n" + fragment);
        }
    }

    if (str.length() > 1) 
    {

        str = removeFirstChar(str, 1);
        List<String> fragments5 = breakWordIntoPiecesFromRightToLeft(str);

        System.out.print("\n");

        for (String fragment : fragments5)
        {
            System.out.print("\n" + fragment);
        }
    }


    if (str.length() > 1)
    {
        str = removeFirstChar(str, 1);
        List<String> fragments6 = breakWordIntoPiecesFromRightToLeft(str);

        System.out.print("\n");

        for (String fragment : fragments6)
        {
            System.out.print("\n" + fragment);
        }
    }

    if (str.length() > 1)
    {
        str = removeFirstChar(str, 1);
        List<String> fragments7 = breakWordIntoPiecesFromRightToLeft(str);
        System.out.print("\n");
        for (String fragment : fragments7)
        {
            System.out.print("\n" + fragment);
        }
    }

    if (str.length() > 1)
    {
        str = removeFirstChar(str, 1);
        List<String> fragments8 = breakWordIntoPiecesFromRightToLeft(str);

        System.out.print("\n");

        for (String fragment : fragments8)
        {
            System.out.print("\n" + fragment);
        }
    }
}

public static List<String> breakWordIntoPiecesFromRightToLeft(String word)
{
    int sizeOfWord = word.length();
    List<String> fragments = new ArrayList<>();

    for (int i = 0; i < word.length() - 1; i++)
    {
         String aFragment = removeLastChar(word, i);
         fragments.add(aFragment);
    }

    return fragments;
}

private static String removeLastChar(String str, Integer i)

{
    return str.substring(0, str.length() - i); //((Line 200)) remove last i letters.
}

public static String removeFirstChar(String s, Integer i)
{
    return s.substring(i); // remove first i letters
}

The output below is correct. It prints all possible substrings in the desired order. This is the desired order, but the code can't be hard-coded.

OUTPUT:

ABIGWORD //First it prints the whole word.
ABIGWOR  //Then the rest below are all possible sub strings lined up in order by letter
ABIGWO
ABIGW
ABIG
ABI
AB

BIGWORD
BIGWOR
BIGWO
BIGW
BIG
BI

IGWORD
IGWOR
IGWO
IGW
IG

GWORD
GWOR
GWO
GW

WORD
WOR
WO

ORD
OR

RD

While this technically works, it's clearly hard coded and therefore only works for words up to around 8 letters or less. The following code below is a refactored version of breakWordIntoPieces() utilizing all other methods above.

public static void breakWordIntoPiecesRefactored(String str)
{
    int subtractCharactersBy = 0;
    int lengthOfWord = str.length();

    while (lengthOfWord > 1) //this if can be checked as the method is called
    {
        str = removeFirstChar(str, subtractCharactersBy); // ((Line 259) 
        lengthOfWord -= 1;
        subtractCharactersBy +=1;
        List<String> fragments = breakWordIntoPiecesFromRightToLeft(str);

        for (String fragment : fragments)
        {
            System.out.print("\n" + fragment);
        }
    }
}

OUTPUT:

ABIGWORD
ABIGWOR
ABIGWO
ABIGW
ABIG
ABI
AB
BIGWORD
BIGWOR
BIGWO
BIGW
BIG
BI
GWORD //Why is it skipping the letter I straight to G?
GWOR
GWO
GW
RD

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out 
of range: -2
at java.lang.String.substring(String.java:1931)
at com.company.Controllers.SpellCheck.removeFirstChar(SpellCheck.java:200)
at 
com.company.Controllers.SpellCheck.breakWordIntoPiecesRefactored(SpellCheck.java:259)
at com.company.Main.main(Main.java:20)

Process finished with exit code 1

I think there's something important with that -2 in the error code. I think it has to do with subtracting more letters to a string than the length of the string itself?


Solution

  • You can use two for loops for this, one nested inside the other. The outer loop moves in from the left, the inner loop moves in from the right.

    static void printPieces(String str, int min)
    {
        for(int i=0; i<=str.length()-min; i++)
        {
            for(int j=str.length(); j>=i+min; j--)
            {
                System.out.println(str.substring(i, j));
            }
            System.out.println();
        }
    }
    

    For printPieces("ABIGWORD", 2) we get:

    ABIGWORD
    ABIGWOR
    ABIGWO
    ABIGW
    ABIG
    ABI
    AB
    
    BIGWORD
    BIGWOR
    BIGWO
    BIGW
    BIG
    BI
    
    IGWORD
    IGWOR
    IGWO
    IGW
    IG
    
    GWORD
    GWOR
    GWO
    GW
    
    WORD
    WOR
    WO
    
    ORD
    OR
    
    RD