两个字符串的公共子串

时间:2013-10-19 06:37:15

标签: java algorithm

这个特别的访谈问题困扰了我:

Given two Strings S1 and S2. Find the longest Substring which is a Prefix of S1 and suffix of S2

通过Google,我遇到了以下解决方案,但并不十分清楚它在做什么。

public String findLongestSubstring(String s1, String s2) {
        List<Integer> occurs = new ArrayList<>();
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) == s2.charAt(s2.length()-1)) {
                occurs.add(i);
            }
        }

        Collections.reverse(occurs);

        for(int index : occurs) {
            boolean equals = true;
            for(int i = index; i >= 0; i--) {
                if (s1.charAt(index-i) != s2.charAt(s2.length() - i - 1)) {
                    equals = false;
                    break;
                }
            }
            if(equals) {
                return s1.substring(0,index+1);
            }
        }

        return null;
    }

我的问题:

  1. 此解决方案如何运作?
    • 你如何发现这个解决方案?
  2. 是否有更直观/更简单的解决方案?

4 个答案:

答案 0 :(得分:4)

问题的第2部分

这是一个较短的变体:

public String findLongestPrefixSuffix(String s1, String s2) {

   for( int i = Math.min(s1.length(), s2.length()); ; i--) {
      if(s2.endsWith(s1.substring(0, i))) {
         return s1.substring(0, i);
      }
   }    
}

我正在使用Math.min来查找最短字符串的长度,因为我不需要也不能比这更长。

someString.substring(x,y)返回从字符x开始读取someString并在字符y处停止时获得的字符串。我从最大可能的子串(s1s2)向后移动到最小的子串,即空字符串。这种方式第一次我的条件成立时,它将是履行它的最大可能子串。

如果你愿意,你可以反过来,但你必须引入一个变量来保存迄今为止满足条件的最长的子串的长度:

public static String findLongestPrefixSuffix(String s1, String s2) {

   if (s1.equals(s2)) { // this part is optional and will 
      return s1;        // speed things up if s1 is equal to s2
   }                    //

   int max = 0;
   for (int i = 0; i < Math.min(s1.length(), s2.length()); i++) {
      if (s2.endsWith(s1.substring(0, i))) {
         max = i;
      }
   }
   return s1.substring(0, max);
}

记录:在后一个示例中,您可以从i = 1开始,以获得额外的性能。除此之外,您还可以使用i来指定后缀至少要达到的时长。 ;)如果您编写Math.min(s1.length(), s2.length()) - x,可以使用x指定找到的子字符串最多可以有多长。对于第一种解决方案,这两种方法都是可能的,但最小长度更多涉及。 ;)


问题的第1部分

Collections.reverse上方的部分中,代码的作者搜索s1s2的最后一个字母所在的所有位置并保存此位置。

以下内容基本上就是我的算法所做的,不同之处在于,他不检查每个子字符串,只检查那些以s2的最后一个字母结尾的子字符串。

这是一种加速的方法。如果速度不是那么重要,我的天真实施就足够了。 ;)

答案 1 :(得分:3)

你在哪里找到解决方案?它是由一个可信的,备受尊敬的编码器写的吗?如果您不确定,那么可能不值得一读。人们可以编写非常复杂和低效的代码来完成非常简单的事情,并且不值得理解算法。

不是试图理解别人的解决方案,而是自己想出它可能更容易。我认为你用这种方式更好地理解问题,逻辑变成你自己的。随着时间的推移和实践,思维过程将开始变得更加自然。实践是完美的。

无论如何,我在Python here中添加了一个更简单的实现(扰乱警报!)。我建议你先自己找出解决方案,然后再将它与我的比较。

答案 2 :(得分:2)

Apache commons lang3,StringUtils.getCommonPrefix()

Java通过stdlib提供有用的东西真的很糟糕。从好的方面来说,Apache几乎总是有一些合理的工具。

答案 3 :(得分:0)

我将@TheMorph的答案转换为javascript。希望这对js开发人员有帮助

if (typeof String.prototype.endsWith !== 'function') {
    String.prototype.endsWith = function(suffix) {
        return this.indexOf(suffix, this.length - suffix.length) !== -1;
    };
}

function findLongestPrefixSuffix(s2, s1) {

   for( var i = Math.min(s1.length, s2.length); ; i--) {
      if(s2.endsWith(s1.substring(0, i))) {
         return s1.substring(0, i);
      }
   }    
}

console.log(findLongestPrefixSuffix('abc', 'bcd')); // result: 'bc'