Проблема, связанная с алгоритмом поиска слов - PullRequest
0 голосов
/ 22 сентября 2019

Кажется, мой код выдает эту ошибку: java.lang.StringIndexOutOfBoundsException: индекс строки вне диапазона: 0 с использованием методов substring и string.charAt ()

Я пытался изменить значения в дляЦикл и в аргументах, поэтому индекс строки не будет проходить мимо его предполагаемых значений

public final class WordSearcher {
    public static void main(String args[]) {
        //String wordLibrary = args[0];
        //String puzzle = args[1];
        int j,k,l;
        String wordLibrary = "TEST,SAMPLE,OUTPUT";
        String puzzle = "TUPTUOELPMAS";
        String puzzleDup = puzzle;
        String[] wordstoFind = wordLibrary.split(",");
        int ifWordExists = 0;
        int puzzleLength = puzzle.length();

        for(k = 0; k< wordstoFind.length; k++) {
            for (l = 0; l< wordstoFind[k].length(); l++) {
                for(j=0; j< puzzleLength; j++) {

                    if(wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
                        ifWordExists++;
                        puzzleDup = puzzleDup.substring(j+1,puzzleDup.length());
                        break;
                    }
                }   
            } 

            if( ifWordExists == wordstoFind[k].length()) 
                    System.out.println(wordstoFind[k]);


                ifWordExists = 0;
                System.out.println(puzzle);
                puzzleDup = puzzle;
        }

            for(k = 0; k< wordstoFind.length; k++) {
                for (l = 0; l< wordstoFind[k].length(); l++) {
                    for(j=puzzleLength-1; j> 0; j--) {

                        if(wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
                            ifWordExists++;
                            puzzleDup = puzzleDup.substring(0,j-1);
                            break;
                        }
                    }     
                } 

                if( ifWordExists == wordstoFind[k].length()) 
                    System.out.println(wordstoFind[k]);


                ifWordExists = 0;
                System.out.println(puzzle);
                puzzleDup = puzzle;
        }
    }
}

Предполагается найти слова, распределенные в строке, переданной в качестве аргумента командной строки.например, XTXXXEXXXXXSXT должен вернуть TEST, если это одно из слов, которые мне нужно найти.

Ответы [ 2 ]

0 голосов
/ 23 сентября 2019

Ваша проблема на самом деле не в строке кода:

puzzleDup = puzzleDup.substring(j + 1, puzzleDup.length());

, а в том, что вы перебираете неправильную строку головоломки в двух определенных местах (циклах) в вашем коде.Давайте возьмем второй внутренний цикл FOR в первом наборе циклов:

for(j = 0; j < puzzleLength; j++) {
    if(wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
        ifWordExists++;
        puzzleDup = puzzleDup.substring(j + 1, puzzleDup.length());
        break;
    }
} 

Здесь вы перебираете длину исходной строки головоломки, но высравнение символов из фактического слова с символами в строке puzzleDup .Сначала переменная puzzleDup будет содержать исходное содержимое фактической строки головоломки, но в том же цикле вы удаляете все найденные символы, которые будут соответствовать символу из проверяемого слова .Делая это удаление, он уменьшает длину puzzleDup , и потому что Puzzle String будет длиннее, чем puzzleDup будет целочисленной переменной j в конечном итоге выйдет за пределы того, что puzzleDup способно обработать, поэтому генерирует StringIndexOutOfBoundsException .

Нижняя строка .... если вы собираетесь сравнивать слово символов с puzzleDup символов и затем изменять puzzleDup во время процессазатем используйте puzzleDup в объявлении цикла:

for (int j = 0; j < puzzleDup.length(); j++) {
    if (wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
        ifWordExists++;
        puzzleDup = puzzleDup.substring(j + 1, puzzleDup.length());
        break;
    }
}

С другой стороны, следующая строка кода

puzzleDup = puzzleDup.substring(j + 1, puzzleDup.length());

на самом деле не настолько эффективна, если только не первый символиз puzzleDup всегда будет сравниваемым персонажем из word .Что произойдет, если это 3-й персонаж в puzzleDup ?Затем вы стираете первые три символа, содержащиеся в puzzleDup ?Что если один из символов, который вы только что удалили, необходим для следующего слова символа?Я полагаю, что идея состоит в том, чтобы удалить только определенный символ в puzzleDup , который соответствует текущему символу word .Эта строка должна быть:

puzzleDup = puzzleDup.substring(0, j) + puzzleDup.substring(j + 1);

Эта строка кода удалит из puzzleDup только символ, равный символу из слова, обрабатываемого в данный момент.Теперь все остальные персонажи остаются в puzzleDup , чтобы иметь возможность сравниваться с ними.Весь внутренний цикл действительно должен выглядеть следующим образом:

for (int j = 0; j < puzzleDup.length(); j++) {
    if (wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
        ifWordExists++;
        puzzleDup = puzzleDup.substring(0, j) + puzzleDup.substring(j + 1);
        break;
    }
}

И в вашем другом наборе циклов FOR, далее в коде, второй внутренний цикл FOR долженвыглядят примерно так:

for (int j = puzzleDup.length() - 1; j > 0; j--) {
    if (wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
        ifWordExists++;
        puzzleDup = puzzleDup.substring(0, j-1) + puzzleDup.substring(j);
        break;
    }
}

Ниже я приведу метод, который может продемонстрировать один из способов выполнения этой конкретной задачи.В этом примере вы предоставляете запятую (,) с разделителем *, 1080 * Words String (так же, как вы уже сделали с примером кода), а также вы предоставляете строку головоломки.Метод возвращает коллекцию List, в которой каждый элемент этого списка будет содержать:

  • Строка, содержащая фактическую перетасованную строку головоломки;
  • Строка тире (-) к тому жедлина строки головоломки, за исключением того, что совпадающие символы из озадаченной строки и символы из обрабатываемого слова также находятся внутри строки, указывающей, где именно в строке головоломки были найдены совпадения;
  • строка, содержащая фактическиеСлово, относящееся к вышеуказанному результату.

Вот метод:

public static List<String> findWordsInString(String wordsToLocate, String inputOrPuzzleString) {
    List<String> resultsList = new ArrayList<>();
    // With the following split regex, it doesn't matter 
    // how the words are comma delimited with regards to
    // spacing.
    String[] wordstoFind = wordsToLocate.split("\\s{0,},\\s{0,}");
    List<String> foundCharList;
    for (int k = 0; k < wordstoFind.length; k++) {
        String libWord = wordstoFind[k];
        /* The following flag is used to prevent a word from being
           found twice within the Puzzle String at different locations.
           If you actually want this feature then comment the below line
           and any code lines related to it.        */
        boolean wordFound = false;  
        foundCharList = new ArrayList<>();
        for (int l = 0; l < libWord.length(); l++) {
            for (int j = 0; j < inputOrPuzzleString.length(); j++) {
                if (libWord.charAt(l) == inputOrPuzzleString.charAt(j)) {
                    // Found a character. Add it and its puzzle index (delimited with a Pipe character) to foundChar.
                    String foundChar = inputOrPuzzleString.substring(j, (j + 1)) + "|" + String.valueOf(j);
                    /* Make sure the Character at Index isn't already in foundCharList.
                       We're not allowed to use the same character at the same puzzle 
                       index more than once in any given word.         */
                    boolean alreadyHave = false;
                    for (String str : foundCharList) {
                        if (str.equals(foundChar)) {
                            alreadyHave = true;
                            break;
                        }
                    }
                    /* Make sure we need the character and it won't be a duplicate to the
                       word makeup. We do this by removing all the characters we've already 
                       found from a copy of the word we're working on then see which characters
                       are remaining. If the current character matches one of the remaining
                       then it's good to keep.         */
                    String tmpWord = libWord;
                    for (String strg : foundCharList) {
                        for (int x = 0; x < tmpWord.length(); x++) {
                            if (strg.split("\\|")[0].equals(tmpWord.substring(x, x + 1))) {
                                tmpWord = tmpWord.substring(0, x) + tmpWord.substring(x + 1);
                                break;
                            }
                        }
                    }
                    if (tmpWord.contains(Character.toString(inputOrPuzzleString.charAt(j))) && !alreadyHave) {
                        foundCharList.add(foundChar);
                    }
                }
                if (foundCharList.size() == libWord.length()) {
                    resultsList.add(showWordMakeupInString(foundCharList, libWord, inputOrPuzzleString));
                    foundCharList.clear();
                    wordFound = true;
                    break;
                }
                if (wordFound) {
                    break;
                }
            }
        }
    }
    return resultsList;
}

Этот метод также требует использования метода-сателлита ** showWordMakeupInString (), который устанавливает строки строкбыть помещенным в возвращенный список.Конечно, вы можете изменить все это так, как вам нравится:

public static String showWordMakeupInString(List<String> list, String word, String puzzleString) {
    String ls = System.lineSeparator();
    String resultString = puzzleString + ls;
    String locations = String.join("", Collections.nCopies(puzzleString.length(), "-"));
    for (int i = 0; i < list.size(); i++) {
        String[] parts = list.get(i).split("\\|");
        char letter = parts[0].charAt(0);
        int atIndex = Integer.parseInt(parts[1]);
        char[] puzzleChars = locations.toCharArray();
        puzzleChars[atIndex] = letter;
        locations = String.valueOf(puzzleChars);
    }
    resultString += locations + ls + word + ls;
    return resultString;
}

Чтобы использовать этот метод, вы можете сделать что-то вроде этого:

// Create the WORD String...
String wordsToFind = "TEST,SAMPLE,OUTPUT,DOG,poop,HELLO,Who's Your Daddy";

// Create the PUZZLE String. Copy the WORD string and 
// paste it below then delete all the commas.
String puzzle = "TESTSAMPLEOUTPUTpoopHELLODOGWho's Your Daddy";

// Shuffle the PUZZLE String...really mix it up :) 
// The shuffleString() method is provided below.
puzzle = shuffleString(puzzle);

// Find the words within the WORD String within the PUZZLE String.
List<String> resultsList = findWordsInString(wordsToFind, puzzle);

// Display the Listed results to Console Window.
for(String strg : resultsList) {
    System.out.println(strg);
}

Если вы запустите приведенный выше пример, вотчто вы должны увидеть в окне консоли:

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
--E--S--------T----T------------------------
TEST

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
--E--S-L--P------A-------------------------M
SAMPLE

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
O--U------P---T----T------------U-----------
OUTPUT

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
O---------------G------------------D--------
DOG

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
-o-------o---p--------------p---------------
poop

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
O-E----L-------------------L--------H-------
HELLO

OoEUWSOL oPOYpT'GAsTEdToaEdLp TyUoSDHPruLhDM
-o--W--- o--Y--'--s--d--a-d-- -y---D--ru-h--
Who's Your Daddy

Ваши результаты, конечно, будут немного отличаться, потому что строка PUZZLE всегда перетасовывается по-разному при каждом запуске.

shuffleString () метод:

public String shuffleString(String input) {
    List<Character> characters = new ArrayList<>();
    for (char c : input.toCharArray()) {
        characters.add(c);
    }
    StringBuilder output = new StringBuilder(input.length());
    while (!characters.isEmpty()) {
        int randChar = (int) (Math.random() * characters.size());
        output.append(characters.remove(randChar));
    }
    return output.toString();
}
0 голосов
/ 22 сентября 2019

Здесь:

for(k = 0; k < wordstoFind.length; k++) {
    for (l = 0; l < wordstoFind[k].length(); l++) {
        for(j = 0; j < puzzleLength; j++) {
            if(wordstoFind[k].charAt(l) == puzzleDup.charAt(j)) {
                ifWordExists++;
                puzzleDup = puzzleDup.substring(j + 1, puzzleDup.length());
                                                ^^^^^
                break;
            }
        }   
    }

Если j равно puzzleLength - 1, j + 1 выйдет за пределы строки.

...