Java 查找给定两个字符串的所有公共子字符串

Java 查找给定两个字符串的所有公共子字符串,java,string,algorithm,suffix-tree,dynamic-programming,Java,String,Algorithm,Suffix Tree,Dynamic Programming,我遇到了一个问题语句,用于查找给定两个子字符串之间的所有公共子字符串,这样在任何情况下都必须打印最长的子字符串。问题陈述如下: 编写一个程序来查找两个给定字符串之间的公共子字符串。但是,不要包括包含在较长公共子字符串中的子字符串 例如,给定输入字符串eatsleepnightxyz和eatsleepabcxyz,结果应为: eatsleep(由于eatsleepnightxyzeatsleepabcxyz) xyz(由于eatsleepnightxyzeatsleepabcxyz) a(由于e

我遇到了一个问题语句,用于查找给定两个子字符串之间的所有公共子字符串,这样在任何情况下都必须打印最长的子字符串。问题陈述如下:

编写一个程序来查找两个给定字符串之间的公共子字符串。但是,不要包括包含在较长公共子字符串中的子字符串

例如,给定输入字符串
eatsleepnightxyz
eatsleepabcxyz
,结果应为:

  • eatsleep
    (由于
    eatsleepnightxyz
    eatsleepabcxyz
  • xyz
    (由于
    eatsleepnightxyz
    eatsleepabcxyz
  • a
    (由于
    eatsleepnightxyz
    eatsleepabcxyz
  • t
    (由于
    eatsleepnightxyz
    eatsleepabcxyz
但是,结果集不应包括来自的
e
eatsleepnightxyz
eatsleepabcxyz
,因为上述
eatsleep
中已经包含了这两个
e
。也不应包括
eat
eat
ats
等,因为这些也都包含在
eatsleep

在这种情况下,您不必使用字符串实用程序方法,如:contains、indexOf、StringTokenizer、split和replace

我的算法如下:我从蛮力开始,当我提高我的基本理解时,我将切换到更优化的解决方案

 For String S1:
     Find all the substrings of S1 of all the lengths
     While doing so: Check if it is also a substring of 
     S2.
试图找出我的方法的时间复杂性

设两个给定字符串为n1字符串和n2字符串

  • S1的子串数显然是n1(n1+1)/2
  • 但是我们必须找到S1子串的平均长度
  • 假设是m。我们会分开找我的
  • 检查m字符串是否为 n-String是O(n*m)
  • 现在,我们正在检查每个m字符串是否是S2的子字符串, 这是一个n2字符串
  • 如上所述,这是一个O(n2 m)算法
  • 然后,整个算法所需的时间为
  • Tn=(S1中的子字符串数)*(字符比较过程的平均子字符串长度时间)
  • 通过进行某些计算,我得出结论 时间复杂度为O(n3 m2)
  • 现在,我们的工作是找到n1的m
  • 尝试根据n1找到m

    总氮=(n)(1)+(n-1)(2)+(n-2)(3)+……+(2) (n-1)+(1)(n)
    其中Tn是所有子字符串的长度之和

    Average是该总和除以生成的子字符串总数

    这是一个简单的求和除法问题,其解如下O(n)

    因此

    我的算法的运行时间是O(n^5)

    考虑到这一点,我编写了以下代码:

     package pack.common.substrings;
    
     import java.util.ArrayList;
     import java.util.LinkedHashSet;
     import java.util.List;
     import java.util.Set;
    
     public class FindCommon2 {
        public static final Set<String> commonSubstrings = new      LinkedHashSet<String>();
    
     public static void main(String[] args) {
        printCommonSubstrings("neerajisgreat", "neerajisnotgreat");
        System.out.println(commonSubstrings);
    }
    
     public static void printCommonSubstrings(String s1, String s2) {
        for (int i = 0; i < s1.length();) {
            List<String> list = new ArrayList<String>();
            for (int j = i; j < s1.length(); j++) {
                String subStr = s1.substring(i, j + 1);
                if (isSubstring(subStr, s2)) {
                    list.add(subStr);
                }
            }
            if (!list.isEmpty()) {
                String s = list.get(list.size() - 1);
                commonSubstrings.add(s);
                i += s.length();
            }
        }
     }
    
     public static boolean isSubstring(String s1, String s2) {
        boolean isSubstring = true;
        int strLen = s2.length();
        int strToCheckLen = s1.length();
        if (strToCheckLen > strLen) {
            isSubstring = false;
        } else {
            for (int i = 0; i <= (strLen - strToCheckLen); i++) {
                int index = i;
                int startingIndex = i;
                for (int j = 0; j < strToCheckLen; j++) {
                    if (!(s1.charAt(j) == s2.charAt(index))) {
                        break;
                    } else {
                        index++;
                    }
                }
                if ((index - startingIndex) < strToCheckLen) {
                    isSubstring = false;
                } else {
                    isSubstring = true;
                    break;
                }
            }
        }
        return isSubstring;
     }
    }
    
    问题:鉴于投入

      S1 = “neerajisgreat”;
      S2 = “neerajisnotgreat”
      S3 = “rajeatneerajisnotgreat”
    
    对于S1和S2,输出应为:
    neerajis
    great
    但对于S1和S3,输出应为:
    neerajis
    raj
    great
    eat
    ,但我还是得到了
    neerajis
    great
    作为输出。我需要弄清楚这件事


    我应该如何设计代码?

    通常,这种类型的子字符串匹配是在称为(发音为try)的单独数据结构的帮助下完成的。最适合此问题的特定变体是。您的第一步应该是接受输入并构建后缀树。然后,您需要使用后缀树来确定最长的公共子字符串,这是一个很好的练习。

    对于任务,最好使用适当的算法,而不是暴力方法。维基百科描述了两种常见的解决方案:和

    动态规划解需要O(nm)时间和O(nm)空间。这是Wikipedia伪代码最长公共子字符串的简单Java翻译:

    public static Set<String> longestCommonSubstrings(String s, String t) {
        int[][] table = new int[s.length()][t.length()];
        int longest = 0;
        Set<String> result = new HashSet<>();
    
        for (int i = 0; i < s.length(); i++) {
            for (int j = 0; j < t.length(); j++) {
                if (s.charAt(i) != t.charAt(j)) {
                    continue;
                }
    
                table[i][j] = (i == 0 || j == 0) ? 1
                                                 : 1 + table[i - 1][j - 1];
                if (table[i][j] > longest) {
                    longest = table[i][j];
                    result.clear();
                }
                if (table[i][j] == longest) {
                    result.add(s.substring(i - longest + 1, i + 1));
                }
            }
        }
        return result;
    }
    
    • eatsleep
      结果很明显:左上角是
      12345678
      对角线条纹
    • xyz
      结果是右下角的
      123
      对角线
    • a
      结果由顶部(第二行,第九列)附近的
      1
      指示
    • t
      结果由左下角附近的
      1
      指示
    左侧、顶部和
    6
    7
    旁边的另一个
    1
    呢?这些不算数,因为它们出现在由
    12345678
    对角线形成的矩形内-换句话说,它们已经被
    eatsleep
    覆盖

    我建议只做一次,什么也不做,只是做桌子。然后,进行第二次循环,从右下角向后迭代,以收集结果集。

    让我们来看看。
    public static Set<String> longestCommonSubstrings(String s, String t) {
        int[][] table = new int[s.length()][t.length()];
        int longest = 0;
        Set<String> result = new HashSet<>();
    
        for (int i = 0; i < s.length(); i++) {
            for (int j = 0; j < t.length(); j++) {
                if (s.charAt(i) != t.charAt(j)) {
                    continue;
                }
    
                table[i][j] = (i == 0 || j == 0) ? 1
                                                 : 1 + table[i - 1][j - 1];
                if (table[i][j] > longest) {
                    longest = table[i][j];
                    result.clear();
                }
                if (table[i][j] == longest) {
                    result.add(s.substring(i - longest + 1, i + 1));
                }
            }
        }
        return result;
    }
    
      e a t s l e e p a b c x y z
    e 1 0 0 0 0 1 1 0 0 0 0 0 0 0
    a 0 2 0 0 0 0 0 0 1 0 0 0 0 0
    t 0 0 3 0 0 0 0 0 0 0 0 0 0 0
    s 0 0 0 4 0 0 0 0 0 0 0 0 0 0
    l 0 0 0 0 5 0 0 0 0 0 0 0 0 0
    e 1 0 0 0 0 6 1 0 0 0 0 0 0 0
    e 1 0 0 0 0 1 7 0 0 0 0 0 0 0
    p 0 0 0 0 0 0 0 8 0 0 0 0 0 0
    n 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    i 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    g 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    h 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    t 0 0 1 0 0 0 0 0 0 0 0 0 0 0
    x 0 0 0 0 0 0 0 0 0 0 0 1 0 0
    y 0 0 0 0 0 0 0 0 0 0 0 0 2 0
    z 0 0 0 0 0 0 0 0 0 0 0 0 0 3