Je voudrais un algorithme (ou une bibliothèque) efficace que je puisse utiliser dans Java pour rechercher des sous-chaînes dans une chaîne.
Ce que je voudrais faire, c'est:
Étant donné une chaîne d'entrée - [~ # ~] instr [~ # ~] :
"BCDEFGH"
Et un ensemble de chaînes candidates - [~ # ~] cand [~ # ~] :
"AB", "CDE", "FG", "H", "IJ"
Rechercher toutes les chaînes [~ # ~] cand [~ # ~] qui correspondent à des sous-chaînes dans [~ # ~] instr [~ # ~]
Dans cet exemple, je ferais correspondre "CDE", "FG" et "H" (mais pas "AB" et "IJ")
Il pourrait y avoir plusieurs milliers de chaînes candidates (dans CAND), mais plus important encore, je ferai cette recherche plusieurs millions de fois, donc j'ai besoin que ce soit RAPIDE.
Je voudrais travailler avec des tableaux de caractères. De plus, je ne suis pas intéressé par les solutions architecturales, comme la distribution de la recherche - juste la fonction/l'algorithme le plus efficace pour le faire localement.
De plus, toutes les chaînes de CAND et INSTR seront toutes relativement petites (<50 caractères) - c'est-à-dire que la chaîne cible INSTR n'est PAS longue par rapport aux chaînes candidates.
Mise à jour J'aurais dû le mentionner, l'ensemble des chaînes CAND est invariant à travers toutes les valeurs de INSTR.
Mise à jour J'ai seulement besoin de savoir qu'il y avait une correspondance - et je n'ai pas besoin de savoir quelle était la correspondance.
Mise à jour finale J'ai choisi d'essayer AhoCorsick et Rabin-Karp, en raison de la simplicité de mise en œuvre. Parce que j'ai des modèles de longueur variable, j'ai utilisé un Rabin-Karp modifié qui hache les n premiers caractères de chaque modèle, où n est la longueur du plus petit modèle, N était alors la longueur de ma fenêtre de recherche de sous-chaîne déroulante. Pour l'Aho Corsick, j'ai utilisé ceci
Dans mon test, j'ai recherché 1000 modèles dans deux articles de journaux, en moyenne sur 1000 itérations, etc. Les temps normalisés pour terminer étaient:
AhoCorsick : 1
RabinKarp : 1,8
Recherche naïve (vérifiez chaque modèle et utilisez string.contains): 50
* Quelques ressources décrivant les algos mentionnés dans les réponses ci-dessous:
http://www.seas.gwu.edu/~simhaweb/cs151/lectures/module5/module5.html
http://www.cs.princeton.edu/courses/archive/spr09/cos226/lectures/18SubstringSearch-2x2.pdf
Lisez sur algorithme Aho-Corasick et algorithme Rabin-Karp .
Si l'entrée n'est pas trop grande, vous ne voulez pas répéter la recherche plusieurs fois et vous n'avez pas beaucoup de modèles, il peut être judicieux d'utiliser plusieurs fois un seul algorithme de modèle. Le article Wikipedia sur les algorithmes de recherche donne de nombreux algorithmes avec des temps d'exécution et de prétraitement.
Implémentations:
Présentations:
Convertissez l'ensemble des chaînes candidates en un automate à états finis déterministes, puis parcourez la chaîne d'entrée en temps linéaire. La conversion d'une seule chaîne en DFS est bien couverte dans les livres standard. Vous pouvez convertir un ensemble de chaînes en construisant d'abord un automate non déterministe, puis en le déterminant. Cela peut créer une explosion exponentielle dans le pire des cas, de la taille de l'automate, mais la recherche par la suite est rapide; surtout si la chaîne cible est longue et les candidats courts, cela va bien fonctionner.
C'est à cela que servent les expressions régulières. Comme indiqué ci-dessus, les automates à états finis sont ce dont vous avez besoin, mais c'est exactement la façon dont un regexp-matcher standard est implémenté.
Dans Java vous pourriez écrire quelque chose comme:
StringBuilder sb = new StringBuilder();
bool first = true;
for (String subStr : substrings) {
if (first)
first = false;
else
sb.append('|');
sb.append(escape(subStr));
}
Pattern p = Pattern.compile(sb.toString());
la méthode escape
devrait échapper tous les caractères qui ont une signification spéciale dans une expression rationnelle.
recherche de motifs multiples Rabin-Karp semble être le plus rapide.
Vous voudrez peut-être examiner algorithme Aho-Corasick et les algorithmes associés. Je ne connais aucune bibliothèque qui implémente cela, mais c'est la manière classique de résoudre ce problème.
Vérifiez également algorithme de Boyer-Moore pour une correspondance de modèle à chaîne unique.
Nous pouvons profiter de la petite taille (<50 caractères) des cordes pour construire un algo super rapide pour ce cas, au prix de la mémoire.
Nous pouvons hacher toutes les sous-chaînes possibles de INSTR en un seul hachage qui coûtera O (n ^ 2) fois. Quel que soit le nombre de chaînes CAND, la recherche sera alors O (1). Ça vaut le coup pour un très grand nombre de chaînes CAND.
Si INSTR est grand, alors nous pouvons construire un tableau de suffixes et ne pas le trier, de sorte que l'élément supérieur soit le plus long (= N) et l'élément inférieur soit le dernier caractère de INSTR. Maintenant, pour chaque chaîne CAND, recherchez uniquement à partir du haut tant que longueur (CAND) <= longueur (suffixe). Chacune de ces comparaisons sera O (n).
import Java.util.Scanner;
public class StringMatch
{
static int temp,i=0,j=0; static boolean flag=true,matcher=false;
static String str=null,mstr=null;static char astr[],amstr[];
static void getter(){
Scanner sc = new Scanner(System.in);
str = sc.nextLine();
//String str="today is Monday";
astr=str.toCharArray();
mstr = sc.nextLine();
//String mstr="is";
amstr=mstr.toCharArray();
}
static void stringMatch(){
while(i<astr.length){
if(astr[i]==amstr[j]){
while((j!=amstr.length)&&flag){temp=i;
if(astr[i]!=amstr[j]) {flag=false;matcher=false;}
else{matcher=true;}
i++;j++;
//System.out.println(i+"\t"+j);
}if(matcher==true)break;i=temp;}i++;j=0;flag=true;
}
if(matcher==true) {System.out.println("true");}
else {System.out.println("false");}
}
public static void main(String[] args) {
StringMatch.getter();
StringMatch.stringMatch();
}
}
Ici sont quelques implémentations d'algorithmes de recherche de chaînes rapides en Java.
Une autre solution consiste à utiliser un tableau de suffixes pour l'instrument [~ # ~] [~ # ~] .
Puisque l'instrument [~ # ~] [~ # ~] est petit, vous pouvez le trier avec le tri à bulles.
Ensuite, vous pouvez rechercher une chaîne spécifique [~ # ~] cand [~ # ~] dans O(logN) = temps,
où N = longueur (suffix_array) = longueur (INSTR).