我遇到了一个问题声明,找到了 给定两个子字符串之间 的 所有公共子字符串,因此 在每种情况下都必须打印最长的子字符串。问题陈述如下:
编写程序以查找两个给定字符串之间的公共子字符串。但是,不要包括较长的公共子字符串中包含的子字符串。 例如,给定输入字符串eatsleepnightxyz和eatsleepabcxyz,结果应为: eatsleep(由于**eatsleep** nightxyz **eatsleep** abcxyz) xyz(由于)eatsleepnight **xyz** eatsleepabc **xyz** a(由于)e **a** tsleepnightxyz eatsleep **a** bcxyz t(由于)eatsleepnigh **t** xyz ea **t** sleepabcxyz 但是,结果集应 不 包括e从 ,因为这两个s的已经包含在上面提到的。但也不应该包括,,等,因为这些也都被覆盖。**e** atsleepnightxyz eatsl **e** epabcxyz``e``eatsleep``ea``eat``ats``eatsleep 在这种情况下,您不必使用String实用程序方法,例如:contains,indexOf,StringTokenizer,split和replace。
编写程序以查找两个给定字符串之间的公共子字符串。但是,不要包括较长的公共子字符串中包含的子字符串。
例如,给定输入字符串eatsleepnightxyz和eatsleepabcxyz,结果应为:
eatsleepnightxyz
eatsleepabcxyz
eatsleep
**eatsleep** nightxyz
**eatsleep** abcxyz
xyz
eatsleepnight **xyz**
eatsleepabc **xyz**
a
e **a** tsleepnightxyz
eatsleep **a** bcxyz
t
eatsleepnigh **t** xyz
ea **t** sleepabcxyz
但是,结果集应 不 包括e从 ,因为这两个s的已经包含在上面提到的。但也不应该包括,,等,因为这些也都被覆盖。**e** atsleepnightxyz eatsl **e** epabcxyz``e``eatsleep``ea``eat``ats``eatsleep
e
**e** atsleepnightxyz
eatsl **e** epabcxyz``e``eatsleep``ea``eat``ats``eatsleep
在这种情况下,您不必使用String实用程序方法,例如: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-String和n2-String
尝试根据n1查找m。
T n =(n)(1)+(n-1)(2)+(n-2)(3)+ ..... +(2)(n-1)+(1)(n) 其中T n是所有子串的长度之和。
平均值将是该总和除以产生的子字符串总数。
这只是一个求和除法问题,其解如下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; } }
我的代码说明:
printCommonSubstrings: Finds all the substrings of S1 and checks if it is also a substring of S2. isSubstring : As the name suggests, it checks if the given string is a substring of the other string.
问题:鉴于输入
S1 = “neerajisgreat”; S2 = “neerajisnotgreat” S3 = “rajeatneerajisnotgreat”
在S1和S2的情况下,输出应该是:neerajis和great ,但在S1和S3的情况下,输出应该是: neerajis,raj,great,eat但还是我得到neerajis和great作为输出。我需要弄清楚这一点。
neerajis
great
raj
eat
我应该如何设计我的代码?
对于任务,使用适当的算法而不是蛮力的方法会更好。Wikipedia描述了最长的常见子字符串问题的两种常见解决方案:后缀树和动态编程。
动态编程解决方案需要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; }
现在,您需要所有常见的子字符串,而不仅仅是最长的。您可以增强此算法以包括较短的结果。让我们检查一下表中的示例输入eatsleepnightxyz和eatsleepabcxyz:
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
12345678
123
1
那其他1的左侧,顶部和旁边的S 6和7?那些不算在内,因为它们出现在12345678对角线形成的矩形内- 换句话说,它们已经被覆盖eatsleep。
6
7
我建议做一遍,只做一张桌子。然后,进行第二遍处理,从右下角向后迭代,以收集结果集。