Как можно улучшить этот Java-код для поиска подстроки в строке? - PullRequest
10 голосов
/ 15 октября 2010

Меня недавно попросили представить решение проблемы для работы.

Задача : Найти подстроку в строке.

Input: "Little star's deep dish pizza sure is fantastic."  
Search: "deep dish pizza"  
Output: "Little star's [[HIGHLIGHT]]deep dish pizza[[ENDHIGHLIGHT]] sure is fantastic."

Обратите внимание, что маркер не должен иметь точно такой же результат в этом примере, поскольку вы определяете, что такое хороший фрагмент, и возвращаете наиболее релевантный фрагмент с выделенными условиями запроса.

Самым важным требованием было написать его так, как я написал бы производственный код .

Мое решение не было принято. Как я мог улучшить это? Я знаю, я мог бы использовать:

  1. Алгоритм Кнута-Морриса-Пратта
  2. Regex (можно?)

Мой ВОПРОС:

  1. Что технические компании принимают во внимание при рассмотрении кода для работы. Я отправил код в тот же день, это помогает?

  2. В одном из комментариев отмечалось, что это похоже на школьный код, а не на производственный код. Как? Есть предложения?

Мое решение:
FindSubString.java

/**
 * FindSubString.java: Find sub-string in a given query
 * 
 * @author zengr
 * @version 1.0
 */

public class FindSubstring {
    private static final String startHighlight = "[[HIGHLIGHT]]";
    private static final String endHighlight = "[[ENDHIGHLIGHT]]";

    /**
     * Find sub-string in a given query
     * 
     * @param inputQuery: A string data type (input Query)
     * @param highlightDoc: A string data type (pattern to match)
     * @return inputQuery: A String data type.
     */
    public String findSubstringInQuery(String inputQuery, String highlightDoc) {
        try {

            highlightDoc = highlightDoc.trim();

            if (inputQuery.toLowerCase().indexOf(highlightDoc.toLowerCase()) >= 0) {
                // update query if exact doc exists
                inputQuery = updateString(inputQuery, highlightDoc);
            }

            else {
                // If exact doc is not in the query then break it up
                String[] docArray = highlightDoc.split(" ");

                for (int i = 0; i < docArray.length; i++) {
                    if (inputQuery.toLowerCase().indexOf(docArray[i].toLowerCase()) > 0) {
                        inputQuery = updateString(inputQuery, docArray[i]);
                    }
                }
            }
        } catch (NullPointerException ex) {
            // Ideally log this exception
            System.out.println("Null pointer exception caught: " + ex.toString());
        }

        return inputQuery;
    }

    /**
     * Update the query with the highlighted doc
     * 
     * @param inputQuery: A String data type (Query to update)
     * @param highlightDoc: A String data type (pattern around which to update)
     * @return inputQuery: A String data type.
     */
    private String updateString(String inputQuery, String highlightDoc) {
        int startIndex = 0;
        int endIndex = 0;

        String lowerCaseDoc = highlightDoc.toLowerCase();
        String lowerCaseQuery = inputQuery.toLowerCase();

        // get index of the words to highlight
        startIndex = lowerCaseQuery.indexOf(lowerCaseDoc);
        endIndex = lowerCaseDoc.length() + startIndex;

        // Get the highlighted doc
        String resultHighlightDoc = highlightString(highlightDoc);

        // Update the original query
        return inputQuery = inputQuery.substring(0, startIndex - 1) + resultHighlightDoc + inputQuery.substring(endIndex, inputQuery.length());
    }

    /**
     * Highlight the doc
     * 
     * @param inputString: A string data type (value to be highlighted)
     * @return highlightedString: A String data type.
     */
    private String highlightString(String inputString) {
        String highlightedString = null;

        highlightedString = " " + startHighlight + inputString + endHighlight;

        return highlightedString;
    }
}

TestClass.java

/**
 * TestClass.java: jUnit test class to test FindSubString.java
 * 
 * @author zengr
 * @version 1.0
 */

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

public class TestClass extends TestCase
{
    private FindSubstring simpleObj = null;
    private String originalQuery = "I like fish. Little star's deep dish pizza sure is fantastic. Dogs are funny.";

    public TestClass(String name) {
        super(name);
    }

    public void setUp() { 
        simpleObj = new FindSubstring();
    }

    public static Test suite(){

        TestSuite suite = new TestSuite();
        suite.addTest(new TestClass("findSubstringtNameCorrect1Test"));
        suite.addTest(new TestClass("findSubstringtNameCorrect2Test"));
        suite.addTest(new TestClass("findSubstringtNameCorrect3Test"));
        suite.addTest(new TestClass("findSubstringtNameIncorrect1Test"));
        suite.addTest(new TestClass("findSubstringtNameNullTest"));

        return suite;
    }

    public void findSubstringtNameCorrect1Test() throws Exception
    {
        String expectedOutput = "I like fish. Little star's deep [[HIGHLIGHT]]dish pizza[[ENDHIGHLIGHT]] sure is fantastic. Dogs are funny.";
        assertEquals(expectedOutput, simpleObj.findSubstringInQuery(originalQuery, "dish pizza"));
    }

    public void findSubstringtNameCorrect2Test() throws Exception 
    {
        String expectedOutput = "I like fish. Little star's [[HIGHLIGHT]]deep dish pizza[[ENDHIGHLIGHT]] sure is fantastic. Dogs are funny.";
        assertEquals(expectedOutput, simpleObj.findSubstringInQuery(originalQuery, "deep dish pizza"));
    }

    public void findSubstringtNameCorrect3Test() throws Exception 
    {
        String expectedOutput = "Hello [[HIGHLIGHT]]how[[ENDHIGHLIGHT]] are [[HIGHLIGHT]]you[[ENDHIGHLIGHT]]r?";
        assertEquals(expectedOutput, simpleObj.findSubstringInQuery("Hello how are your?", "how you"));
    }

    public void findSubstringtNameIncorrect1Test() throws Exception 
    {
        String expectedOutput = "I like fish. Little star's deep dish pizza sure is fantastic. Dogs are funny.";
        assertEquals(expectedOutput, simpleObj.findSubstringInQuery(originalQuery, "I love Ruby too"));
    }

    public void findSubstringtNameNullTest() throws Exception
    {
        String expectedOutput = "I like fish. Little star's deep dish pizza sure is fantastic. Dogs are funny.";
        assertEquals(expectedOutput, simpleObj.findSubstringInQuery(originalQuery, null));

    }
}

Ответы [ 6 ]

8 голосов
/ 15 октября 2010

Несколько комментариев;

  • Вы только выделяете вхождение first строки поиска.
  • Вы полагаете, что соответствие нижнего регистра подходит.Если это не было указано в качестве требования, возможно, было бы лучше предоставить два метода: один, который учитывает регистр, и другой, который игнорирует регистр.
  • Я бы, вероятно, проверил заданные параметры и бросил бы NPE, если один из них был бы нулевым.Это было бы первым, что сделал мой метод.Я бы четко задокументировал это поведение в javadoc.
  • Ваш метод мама плохой;Главная задача findSubstringInQuery не найти, а выделить, а часть inQuery излишня.Просто вызовите метод highlight или, возможно, highlightIgnoreCase, если у вас будет highlight, который учитывает регистр.
  • Ваши параметры параметров метода неверны.Я посмотрел на сигнатуру вашего метода 10 раз и все еще должен посмотреть на тело метода, чтобы напомнить себе, какой аргумент является поисковым термином, а какой - текстом для поиска.Назовите их searchTerm и text.
  • Рабочий код не использует пакет по умолчанию.
  • Рабочий код не использует System.out.println().
  • Ваш Javadocнуждается в улучшении, он должен сказать пользователю обо всем , что ему нужно знать о коде.
  • Я бы рассмотрел использование статических методов для класса без переменных класса.
  • Я бы также рассмотрел возможность предоставления пользователю возможности указывать свои собственные маркеры начальной и конечной подсветки (я бы не использовал статические методы, если бы сделал это).
  • Я бы не стал trim(), если бы это не было указано в качестве требования,Если бы я это сделал, то, очевидно, такое поведение было бы задокументировано в javadoc.

Я бы не стал беспокоиться об алгоритме, используемом для поиска, Кнут-Моррис-Пратт выглядит хорошо, но они не должны ожидать васзнать об этом и внедрять его, если в спецификациях вакансий не запрашивается опыт / знания в поиске строк.

3 голосов
/ 15 октября 2010

Если бы этот код был представлен мне для проверки, я бы подумал:

  • Код слишком многословен и сложен там, где его не требуется. Все это можно сделать небольшим методом из десяти строк кода, включая все проверки работоспособности. Этот метод, вероятно, должен быть статическим.
  • Ваш код делает то, о чем (я полагаю) не просили. Вас попросили найти подстроку в строке. Если он не найден, то это нормально - не нужно разбивать подстроку на слова и искать каждое отдельное слово.
  • Если бы они не попросили вас удалить начальные и конечные пробелы, я бы не позвонил trim()
  • Я бы не включил вызовы к toLowerCase(), если бы не было явного запроса, хотя я бы добавил комментарий о том, что они могут быть добавлены в случае необходимости. В любом случае, даже если в поиске не учитывается регистр, в вашем коде слишком много избыточных вызовов на toLowerCase().
  • Вам не нужно ловить NullPointerException - вместо этого вы должны убедиться, что это исключение никогда не выдается.
2 голосов
/ 16 декабря 2011

Похоже, вы упустили суть проблемы. Оригинальное постановка задачи гласит:

Обратите внимание, что маркер не должен иметь точно такой же результат в этом примере, так как вы определяете, что такое хороший фрагмент и вернуть наиболее релевантный фрагмент с выделенными условиями запроса.

Похоже, они хотели, чтобы вы идентифицировали хороший фрагмент кода, который нужно вернуть, а не просто выделяли слова в исходном вводе. Если ввод был длинным, вам нужно вернуть фрагмент текста с выделенными словами.

Один из возможных подходов:

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

Такие вещи, как хорошая идентификация предложений, определение слов и исправление орфографии, также могут находиться в пределах досягаемости, особенно если вам разрешено использовать сторонние библиотеки.

1 голос
/ 15 октября 2010

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

class MarkedText {
    String highlightDoc;
    String inputQuery;
    List<Range> ranges;
}

class Range {
    int offset;
    int length;
}

public MarkedText findSubstringInQuery(String inputQuery, String highlightDoc) {
    [...]
}
1 голос
/ 15 октября 2010

Я не знаю, что я что-то упустил, но я бы написал простой блок кода, используя indexOf () и другие строковые методы. В зависимости от решения проблемы, я бы, вероятно, использовал метод StringBuilder.insert () для добавления выделения. Я бы не стал заниматься токенизацией и зацикливанием, как вы сделали здесь. Я бы оправдал это, сказав, что это самое простое решение проблемы, как указано. KISS - лучший подход к таким вопросам.

Хотя они дали вам входы и выходы, они указали, что должно было произойти, если входы изменились и не было совпадения?

Я также замечаю перехват нулевого указателя. Хорошая идея, если вы знаете, где это может произойти, и намереваетесь что-то с этим сделать. Но так как вы только регистрируетесь, возможно, вам следовало сделать более общий улов. Например, может ли ваш код вызвать исключение IndexOutOfBoundsException. Так что я бы хотел поймать Exception или Throwable.

Еще одна вещь, которую я хотел бы спросить, - это определение того, что они считают «производственным кодом». На первый взгляд это звучит достаточно просто, но мой опыт показывает, что это можно интерпретировать по-разному.

Настоящая проблема в том, что они будут ожидать определенных вещей, а вы их не знаете. Таким образом, вы кодируете то, что работает для вас, и надеетесь, что оно соответствует тому, что они ожидают.

0 голосов
/ 15 октября 2010

Вы имеете в виду совпадение по частичным словам в случае, когда вы разбиваете запрос? Вы также не учитываете возможность того, что строка поиска содержит слово «HIGHLIGHT».

Например, попробуйте это:

Исходные данные: "Пицца для глубокой тарелки маленькой звезды, безусловно, фантастическая."
Поиск: "СВЕТ"
Вывод: (вероятно, не то, что вы хотите)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...