【问题标题】:How can this Java code be improved to find sub-string in a string?如何改进此 Java 代码以在字符串中查找子字符串?
【发布时间】:2011-04-25 18:29:29
【问题描述】:

我最近被要求提交一份工作问题的解决方案。

问题:在字符串中查找子字符串。

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."

请注意,在此示例中,荧光笔不必具有完全相同的结果,因为 正在定义什么是好的 sn-p 并返回最相关的 sn-p查询词突出显示。

最重要的要求是像编写生产代码一样编写它。

我的解决方案未被接受。我怎么能改进它?我知道,我可以使用:

  1. Knuth–Morris–Pratt algorithm
  2. 正则表达式(可以吗?)

我的问题:

  1. 科技公司在审查工作代码时会考虑哪些因素。我在同一天提交了代码,这有什么帮助吗?

  2. 它指出,在其中一个 cmets 中,它看起来像学校代码而不是生产代码。如何?有什么建议吗?

我的解决方案:
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));

    }
}

【问题讨论】:

  • @zengr:您是否在没有 Internet 访问的情况下编写了解决方案?如果是这样,天哪,他们应该雇用你!
  • 我没有。这是我写的。
  • 我认为它没有被接受,因为你忘了先检查 highlightDoc.length() 是否小于 inputQuery
  • 我不会捕获 NullPointerException - 我肯定会检查先决条件。这可能是他们做出决定的原因吗?
  • 想法:主要的 findSubstringInQuery 方法可能是静态的;如果没有指定 trim() 和不区分大小写,我就不会包含它们;它看起来更像是一所学校而不是生产代码——也许他们正在寻找优雅?但是,我喜欢单元测试覆盖率。我会雇用你的!

标签: java algorithm string


【解决方案1】:

听起来你错过了问题的重点。原始问题陈述说:

请注意,荧光笔不必具有完全相同的结果 在这个例子中,因为你正在定义一个好的 sn-p 是什么,并且 返回最相关的 sn-p 并突出显示查询词。

听起来他们希望您识别一个好的 sn-p 以返回,而不仅仅是突出显示原始输入中的单词。如果输入很长,您可能希望返回带有突出显示的单词的较小的 sn-p 文本。

一种可能的方法是:

  • 找到包含所有输入(或 最可能的输入)。
  • 如果可能,扩展 sn-p 以包含封闭的句子。
  • 如果结果大于最大长度,例如 100,则修剪
    sn-p,可能通过在其中的某处插入省略号。

诸如良好的句子识别、词干提取和拼写更正之类的事情也可能在范围内,尤其是在您被允许使用第三方库的情况下。

【讨论】:

    【解决方案2】:

    几厘米;

    • 您只突出显示搜索字符串的第一次
    • 您假设小写匹配没问题。除非这被指定为要求,否则最好提供两种方法,一种尊重大小写,另一种忽略大小写。
    • 我可能会检查给定的参数并在其中任何一个为空时抛出 NPE。这将是我的方法所做的第一件事。我会在 javadoc 中清楚地记录这种行为。
    • 你的方法很糟糕; findSubstringInQuery 的主要任务不是查找,而是突出显示,inQuery 部分是多余的。只需调用方法highlighthighlightIgnoreCase,如果您要拥有一个尊重大小写的highlight
    • 您的方法参数名称错误。我已经查看了您的方法签名 10 次,但仍然需要查看方法主体以提醒自己哪个 arg 是搜索词,哪个是要搜索的文本。打电话给他们searchTermtext
    • 生产代码不使用默认包。
    • 生产代码不使用System.out.println()
    • 您的 javadoc 需要改进,它需要告诉用户他们需要了解的有关代码的一切
    • 我会考虑对没有类变量的类使用静态方法。
    • 我还会考虑允许用户指定他们自己的开始和结束突出显示标记(如果我这样做,我不会使用静态方法)。
    • 我不会trim() 除非这被指定为要求。如果我这样做了,那么显然这种行为将记录在 javadoc 中。

    我不会担心用于搜索的算法,Knuth-Morris-Pratt 看起来不错,但他们不应该指望您知道并实施它,除非工作规范特别要求字符串搜索方面的经验/专业知识。

    【讨论】:

    • 补充: • 您无需提及所有方法参数和返回值都是data type String。从代码中可以看出。 • 当您不知道如何处理该异常时,不应捕获NullPointerException
    • • 您的 JUnit 测试使用旧的 JUnit3 框架。此外,测试方法的名称应以test... 开头。这样您就不需要手动构建测试套件,而只需使用任何合适的 IDE 运行测试即可。
    • • 补充一点:您的代码作弊且错误。考虑一下:assertEquals("B[[HIGHLIGHT]]oo[[ENDHIGHLIGHT]]m", simpleObj.findSubstringInQuery("Boom", "oo"));
    【解决方案3】:

    如果将此代码提交给我进行审核,我会这样想:

    • 代码过于冗长和复杂,但并不需要如此。整个事情可以用十行代码的一个小方法完成,包括所有的健全性检查。这个方法应该是静态的。
    • 您的代码正在执行(我假设)未要求的事情。您被要求在字符串中搜索子字符串。如果没有找到,那很好——无需将子字符串拆分为单词并搜索每个单词。
    • 除非他们要求您删除前导和尾随空格,否则我不会调用 trim()
    • 除非明确要求,否则我不会包括对toLowerCase() 的调用,尽管我会添加一条评论,说明如果需要可以添加它们。无论如何,即使搜索不区分大小写,您的代码中也有太多对 toLowerCase() 的冗余调用。
    • 您不需要捕获 NullPointerException -- 相反,您应该确保永远不会引发此异常。

    【讨论】:

    • 但是您的第 1 点会遵守“生产代码”吗?
    • 是的。 “生产代码”并不一定意味着“很多代码行”。
    • 将方法设为静态会使 JIT 容易将该方法视为内联方法......所以如果多次调用该方法,那不是很糟糕吗?此外,由于它是生产代码,因此应该以可以扩展的方式设计此类。静态方法不能通过子类化进行扩展。那么静态真的好吗?
    • JIT 可能比你我更了解 :)
    • 关于子类化:并非所有内容都需要扩展或覆盖。在字符串中搜索子字符串的简单方法可能不需要。在任何情况下,我都会尝试坚持 KISS 原则,并提供最简单的解决方案,既能满足预期,又能保持生产质量。避免过度设计。
    【解决方案4】:

    正如他们似乎强调的那样,您可以定义什么是好的输出......也许您如何进行解析并不是他们想知道的。也许他们希望您意识到用标记标记字符串中的文本并不是一个很好的解决方案。如果结果要在程序中用于进一步处理,这样的事情可能更合适。

    class MarkedText {
        String highlightDoc;
        String inputQuery;
        List<Range> ranges;
    }
    
    class Range {
        int offset;
        int length;
    }
    
    public MarkedText findSubstringInQuery(String inputQuery, String highlightDoc) {
        [...]
    }
    

    【讨论】:

      【解决方案5】:

      我不知道我是否遗漏了什么,但我会使用 indexOf() 和其他字符串方法编写一个简单的代码块。根据问题的定义,我可能会使用 StringBuilder.insert() 方法来注入突出显示。我不会像您在这里所做的那样进行标记化和循环。我会说这是指定问题的最简单解决方案来证明它的合理性。 KISS 是解决此类问题的最佳方法。

      虽然他们已经为您提供了输入和输出,但他们是否指定了如果输入更改并且没有匹配项会发生什么?

      我还注意到空指针的捕获。如果您知道它可能发生的位置并打算对此采取措施,那是个好主意。但是由于您只是在记录,也许您应该做一个更一般的捕获。例如,您的代码能否触发 IndexOutOfBoundsException。所以我希望捕捉 Exception 或 Throwable。

      我要问的另一件事是他们认为的“生产代码”的定义。乍一看,这听起来很简单,但我的经验是它可以用多种不同的方式来解释。

      真正的问题是他们会期待某些事情,而你却不知道。因此,您编写适合您的代码并希望它符合他们的期望。

      【讨论】:

      • I/P 和 O/P 可以是任何东西,我认为(相当假设)使用catch(Exception ex) 是一种不好的做法。我一直用Exception,但是为了让代码看起来更专业,我改成了NullPointerException
      • 对此会有无数种不同的看法,但在应用程序的顶层,例如public static void main(String args[]) 方法,我总会有一个异常捕获。但在我的应用程序中,我总是会处理特定的异常。关于捕获和包装异常以及是否应该包装抛出的异常并重新抛出等问题也有很多意见。也许问题之一可能不是您捕获了 NPE,而是评论没有解释为什么。我只是猜测:-)
      • @Derek...捕获一般异常可能不是一个理想的解决方案,因为如果它必须是生产代码,则必须对其进行维护...如果您使用一般异常,那么您应该有足够的文档让下一个人知道 try catch 块的用途......添加检查是一个很好的解决方案,但我不会使用一般异常。
      • +1 从最简单的解决方案开始。也许他们希望你问他们一些问题来澄清行为(异常处理、空输入等......),而不仅仅是自己决定......
      • 我同意 KISS 部分。我认为捕捉 NPE 根本不是一个好主意(但捕捉 ExceptionThrowable 在我看来并不会让它变得更好)。
      【解决方案6】:

      您的意思是在拆分查询的情况下匹配部分单词?您也没有考虑搜索字符串包含“HIGHLIGHT”一词的可能性。

      例如,试试这个:

      输入:“小星星的深盘披萨肯定很棒。”
      搜索:“亮点”
      输出:(可能不是你想要的)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 2021-12-01
        • 2014-02-04
        • 2019-10-01
        • 1970-01-01
        • 2011-07-13
        相关资源
        最近更新 更多