【问题标题】:Find longest common prefix?找到最长的公共前缀?
【发布时间】:2011-12-23 10:27:28
【问题描述】:

在两个字符串中:

“玛丽有一只小羊羔”
“玛丽有一只大羊羔”

应该返回

“玛丽有一个”

【问题讨论】:

  • 这里给出的例子很容易理解,但是如果字符串是“ababac”,那我怎么能找到最长的公共前缀呢?

标签: java


【解决方案1】:

您不需要使用StringBuilder - 只需返回子字符串:

public String greatestCommonPrefix(String a, String b) {
    int minLength = Math.min(a.length(), b.length());
    for (int i = 0; i < minLength; i++) {
        if (a.charAt(i) != b.charAt(i)) {
            return a.substring(0, i);
        }
    }
    return a.substring(0, minLength);
}

【讨论】:

  • 简单优雅
【解决方案2】:

Apache Commons 来救援!

org.apache.commons.lang3.StringUtils.getCommonPrefix

...并将source code 与 dyross 的巧妙/勇敢的努力进行比较(目前投票最高)。但是她/他的代码,虽然很好,但只处理两个Strings。这可以处理任何数字。

除了不重新发明轮子之外,我可以想到使用 Apache Commons 总是最适合这类事情的两个原因。

  1. 可以指望 Apache 工程师开发了经过全面测试的代码,可以处理任何问题
  2. 这意味着您不会用愚蠢的实用方法弄乱漂亮的代码。相反,您可以继续做一些有趣的事情。

如果整个给定的 Apache Commons 模块对于您的上下文来说确实太多(它们通常只有几 kB,但没关系),您可以从源代码中提取您需要的位(假设这符合许可证)。在这种情况下indexOfDifference 是必不可少的功能...

【讨论】:

  • 你的第二个论点的推论:它减少了你的代码库,这是一件非常好的事情
【解决方案3】:
String str1;
String str2;
// assuming str1.length > str2.length
  1. a.startsWith(b) == 真 如果没有
  2. 在循环中不断删除 str1 中的最后一个字符并重复检查步骤 1。

【讨论】:

    【解决方案4】:
    public class Test{
     public static void main(String[] args){
        String s1 = "Mary Had a Little Lamb";
        String s2 = "Mary Had a Big Lamb";
        int minStrLen = s1.length();
        if ( minStrLen > s2.length()){
            minStrLen = s2.length();
        }
    
        StringBuilder output = new StringBuilder();
        for(int i=0; i<minStrLen; i++){
            if ( s1.charAt(i) ==  s2.charAt(i)){
             output.append(s1.charAt(i));
            }else{
              break;
            }
        }       
        System.out.println(output.toString());
      }
    }
    

    这可能不是最佳解决方案,但这很容易理解和编程。

    我从merge-sort算法的列表合并技术中借用了这个想法。如果你很少阅读列表合并技术,你会更好地理解我的算法的逻辑。

    【讨论】:

    • 您不需要 StringBuilder,只需返回子字符串。查看我的解决方案。
    【解决方案5】:

    此解决方案适用于多字符串数组。当您有 3 或 4 个字符串时,最好使用 StringBuilder。对于 2 个字符串,可以使用子字符串。 C# 中的代码:

        public string LongestCommonPrefix(string[] strs) {
        if(strs.Length == 0) return string.Empty;
    
        Array.Sort(strs);
    
        var first = strs[0];
        var last = strs[strs.Length - 1];
    
        var sb = new StringBuilder();
        for(int i = 0; i< first.Length; i++)
        {
            if(first[i] != last[i])
            {
                break;
            }
            sb.Append(first[i]);
        }
    
        return sb.ToString();
    }
    

    【讨论】:

    • 为什么要使用builder?对索引进行操作并以子字符串为结果就足够了。
    • @TimurMilovanov 好点。只需找到打破循环的 i 即可。
    【解决方案6】:

    一个非常简单的解决方案可以是:

      public static String longestCommonPrefix(String[] strs) {
        if (strs.length == 0) {
            return "";
        }
        String result = strs[0];
        for (int i = 1; i < strs.length; i++) {
            while (!strs[i].startsWith(result)) {
                result = result.substring(0, result.length() - 1);
            }
        }
        return result;
    }
    

    【讨论】:

      【解决方案7】:

      我已经编写了一个示例代码。希望它会帮助你。下面是代码。

      import java.util.Arrays;
      import java.util.Collections;
      
      public class MaxPrefixString {    
      
       public static void main(String[] args) {
      
        int count = 0;
      
        String [] input= {"Java is a Programming Language",
                          "Java is a OO",
                          "Java is a Platform independent language",
                          "Java is a my favourite"};
      
        Collections.sort(Arrays.asList(input));  
        Integer [] length = new Integer[input.length];
      
        for(int j= 0; j<input.length ; j++){
         length[j] = input[j].length();
        }
        Collections.sort(Arrays.asList(length));
      
        for(int i=0;i<length[0];i++){
      
         if(isValidPrefix(input[0].substring(0, i), input)){
          count++;
         }else{
          break;
         }
        }
        System.out.println("Answer:"+input[0].substring(0, count-1));
       }
      
       private static boolean isValidPrefix(String str,String []input){
        boolean istrueForAll = true;
      
        for(int i = 0;i<input.length;i++){
         istrueForAll = istrueForAll && check(input[i], str);
        }
        return istrueForAll;
       }
      
       private static boolean check(String input,String str){
        return input.contains(str);
       }
      }
      

      如果我错过了任何场景,请告诉我。 click here

      【讨论】:

        【解决方案8】:

        一个非常没有效率,可能会受到很多批评。但我想出了这个想法,令人惊讶的是它满足了我所有的测试用例。它甚至完成了你打算做的工作。容易明白。请对它发表评论,让我知道如何改进它。 (欢迎各种评论和改进)

        class Solution {
        public String longestCommonPrefix(String[] strs) {
            int i;
            StringBuilder sb = new StringBuilder("");
            if(strs.length == 0 ){
                return sb.toString();
            }
            for(int j = 0 ; j< strs[0].length();j++)
            {
                int count=0;
                for(String word: strs){
                    try{  
                        word.charAt(j);  
                    }catch(Exception e){
                        return sb.toString();
                    }      
                    if(word.charAt(j) == strs[0].charAt(j) ){
                        count ++;
                    }else{
                        return sb.toString();
                    }
                    if(count == strs.length){
                        sb.append(word.charAt(j));
                    }
                }
            }
            return sb.toString();
        }}
        

        【讨论】:

          【解决方案9】:

          运行时间:2 毫秒,比最长公共前缀的 Java 在线提交的 35.59% 快。内存使用:37.2 MB,不到最长公共前缀的 Java 在线提交的 62.51%。 注意:与手动排序相比,Arrays.sort() 会占用一些额外的毫秒数!

              public String longestCommonPrefix(String[] strs) {
              
              if (strs.length == 0) {
                  return "";
              }
              
              if (strs.length == 1) {
                  return strs[0];
              }
              
              int minLen = Integer.MAX_VALUE;
              
              for ( int i = 0; i < strs.length; ++i ) {
                  
                  minLen = Math.min(minLen, strs[i].length());
              }
              
              int maxLen = 0;
              
              for (int i = minLen; i >= 0; --i)  {
                  
                  boolean areAllStringsHasSamePrefix = true;
                  String prefix = strs[0].substring(0, i);
                  
                  for ( int j = 0; j < strs.length; ++j) {
                  
                      if ( !strs[j].startsWith(prefix) ) {
                          areAllStringsHasSamePrefix = false;
                          break;
                      }
                  }
                  
                  if ( areAllStringsHasSamePrefix ) {
                      maxLen = Math.max(maxLen, i);
                  }
              }
              
              return strs[0].substring(0, maxLen);
              
          }
          

          【讨论】:

            【解决方案10】:

            简单地说,用 org.apache.commons.lang3:

            1. 添加依赖:
            implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
            
            1. 写代码:
            String a = "";
            String b = "";
            String prefix = StringUtils.getCommonPrefix(a, b);
            

            来自doc 的示例,但注意到在最新版本中您不需要将字符串包装在数组中,因为现在它需要 Varargs:

             StringUtils.getCommonPrefix(null) = ""
             StringUtils.getCommonPrefix(new String[] {}) = ""
             StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
             StringUtils.getCommonPrefix(new String[] {null, null}) = ""
             StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
             StringUtils.getCommonPrefix(new String[] {"", null}) = ""
             StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
             StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
             StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
             StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
             StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
             StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
             StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
             StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
             StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
             StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
             StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
            

            【讨论】:

              【解决方案11】:

              使用二分查找。尝试比较整个字符串。如果它们不相等,请尝试比较第一个字符。如果它们相等,请尝试拆分字符串 (substring(0, str.length()/2)。等等等等。

              【讨论】:

              • 如果公共前缀是n,无论如何都需要比较前n个字符。进行二分搜索是多余的,可能会导致额外的比较。
              猜你喜欢
              • 2021-09-11
              • 1970-01-01
              • 2022-11-22
              • 2020-07-05
              • 2018-09-30
              • 2013-04-14
              • 2012-02-01
              • 2020-02-10
              • 2021-10-12
              相关资源
              最近更新 更多