Je suis tombé sur une déclaration de problème pour trouver le toutes les sous-chaînes communes entre les deux sous-chaînes données de telle sorte que dans tous les cas, vous devez imprimer la plus longue sous-chaîne. L'énoncé du problème est le suivant:
Écrivez un programme pour trouver les sous-chaînes communes entre les deux chaînes données. Cependant, n'incluez pas de sous-chaînes contenues dans des sous-chaînes communes plus longues.
Par exemple, étant donné les chaînes d'entrée
eatsleepnightxyz
eteatsleepabcxyz
, les résultats devraient être:
eatsleep
(en raison deeatsleepnightxyz
eatsleepabcxyz
)xyz
(en raison deeatsleepnightxyz
eatsleepabcxyz
)a
(en raison deeatsleepnightxyz
eatsleepabcxyz
)t
(en raison deeatsleepnightxyz
eatsleepabcxyz
)Cependant, le jeu de résultats doit pas inclure
e
deeatsleepnightxyz
eatsleepabcxyz
, car les deuxe
sont déjà contenus dans leeatsleep
mentionné ci-dessus. Vous ne devez pas non plus inclureea
,eat
,ats
, etc., car ceux-ci sont également tous couverts pareatsleep
.Dans ce cas, vous n'avez pas à utiliser les méthodes de l'utilitaire String comme: contains, indexOf, StringTokenizer, split et replace.
Mon algorithme est le suivant: je commence par la force brute et je passerai à une solution plus optimisée lorsque j'améliorerai ma compréhension de base.
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.
Essayez de comprendre la complexité temporelle de mon approche.
Soit les deux chaînes données n1-String et n2-String
Essayez de trouver m en termes de n1.
Tn = (n) (1) + (n-1) (2) + (n-2) (3) + ..... + (2) (n-1) + (1) (n)
où Tn est la somme des longueurs de toutes les sous-chaînes.
La moyenne sera la division de cette somme par le nombre total de sous-chaînes produites.
Ceci est simplement un problème de sommation et de division dont la solution est la suivante O (n)
Donc...
Le temps d'exécution de mon algorithme est O (n ^ 5).
Dans cet esprit, j'ai écrit le code suivant:
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;
}
}
Explication pour mon code:
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.
Problème: compte tenu des intrants
S1 = “neerajisgreat”;
S2 = “neerajisnotgreat”
S3 = “rajeatneerajisnotgreat”
Dans le cas de S1 et S2, la sortie devrait être: neerajis
et great
mais dans le cas de S1 et S3, la sortie aurait dû être: neerajis
, raj
, great
, eat
mais je reçois toujours neerajis
et great
en sortie. Je dois comprendre cela.
Comment dois-je concevoir mon code?
Vous feriez mieux de disposer d'un algorithme approprié pour la tâche plutôt que d'une approche par force brute. Wikipédia décrit deux solutions courantes au problème de sous-chaîne commun le plus long : arborescence de suffixes et programmation dynamique .
La solution de programmation dynamique prend O ( nm ) temps et O ( nm ) espace. C'est à peu près une simple Java du pseudocode Wikipedia pour la sous-chaîne commune la plus longue:
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;
}
Maintenant, vous voulez toutes les sous-chaînes courantes, pas seulement les plus longues. Vous pouvez améliorer cet algorithme pour inclure des résultats plus courts. Examinons le tableau pour les exemples d'entrées eatsleepnightxyz
et 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
eatsleep
est évident: c'est le 12345678
séquence diagonale en haut à gauche.xyz
est le 123
diagonale en bas à droite.a
est indiqué par le 1
en haut (deuxième ligne, neuvième colonne).t
est indiqué par le 1
en bas à gauche.Et les autres 1
s à gauche, en haut et à côté de 6
et 7
? Ceux-ci ne comptent pas car ils apparaissent dans le rectangle formé par le 12345678
diagonal - en d'autres termes, ils sont déjà couverts par eatsleep
.
Je recommande de faire un passage en ne faisant que construire la table. Ensuite, effectuez un deuxième passage, en itérant vers l'arrière à partir du bas à droite, pour rassembler l'ensemble de résultats.
Typiquement, ce type de correspondance de sous-chaînes est effectué à l'aide d'une structure de données distincte appelée Trie (essai prononcé). La variante spécifique qui convient le mieux à ce problème est un arborescence de suffixes . Votre première étape devrait être de prendre vos entrées et de construire un arbre de suffixes. Ensuite, vous devrez utiliser l'arborescence des suffixes pour déterminer la sous-chaîne commune la plus longue, ce qui est un bon exercice.