如何改进此Java代码以在字符串中查找子字符串?

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

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

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. Knuth-Morris-Pratt算法
  2. 正则表达式(我可以吗?)

我的问题:

  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  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)); } } 

一些评论;

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

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

如果这个代码提交给我审查,这就是我想的:

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

听起来你错过了问题的关键点。 最初的问题陈述说:

请注意,荧光笔在此示例中不必具有完全相同的结果,因为您正在定义好的代码段,并返回最相关的代码段,并突出显示查询字词。

听起来他们希望你找到一个好的片段来回归,而不仅仅是突出显示原始输入中的单词。 如果输入很长,您需要返回一小段带有突出显示单词的文本片段。

一种可能的方法是:

  • 找到包含所有输入(或最可能的输入)的最小文本片段。
  • 如果可能,展开代码段以包含封闭句子。
  • 如果结果大于最大长度,比如100,则修剪
    片段,可能是通过在其中的某处插入省略号。

良好的句子识别,词干和拼写更正等内容也可能在范围内,特别是如果您被允许使用第三方库。

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

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

我也注意到了一个空指针。 如果你知道它可能发生在哪里并且打算对它做些什么,那就好了。 但是,由于你只是记录,也许你应该做一个更普遍的捕获。 例如,您的代码是否可以触发IndexOutOfBoundsException。 所以我想要捕捉Exception或Throwable。

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

真正的问题是他们会期待某些事情,而你却不了解它们。 所以你编写适合你的代码并希望它符合他们的期望。

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

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

你打算在分解查询的情况下匹配部分单词吗? 您也没有考虑搜索字符串包含单词“HIGHLIGHT”的可能性。

例如,试试这个:

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