Existe-t-il une structure de données «pile de chaînes» qui prend en charge ces opérations de chaîne?

28

Je recherche une structure de données qui stocke un ensemble de chaînes sur un jeu de caractères , capable d'effectuer les opérations suivantes. On note en tant que structure de données stockant l'ensemble des chaînes .D ( S ) SΣD(S)S

  • Add-Prefix-Seton : étant donné un ensemble de chaînes (éventuellement vides), dont la taille est limitée par une constante et dont les longueurs de chaîne sont limitées par une constante, retourne . Ces deux constantes de délimitation sont globaux: ils sont les mêmes pour toutes les entrées .T D ( { t s | t T , s S } ) TD(S)TD({ts | tT,sS})T
  • Get-Prefixeson : return . Notez que je ne me soucie pas vraiment de la structure utilisée pour cet ensemble, tant que je peux énumérer son contenu en temps .{ a | a s S , a Σ } O ( | Σ | )D(S){a | asS,aΣ}O(|Σ|)
  • Remove-Prefixeson : return .D ( { s | a s S , a Σ } )D(S)D({s | asS,aΣ})
  • Merge: étant donné et , retournez .D ( T ) D ( S T )D(S)D(T)D(ST)

Maintenant, j'aimerais vraiment faire toutes ces opérations en temps , mais je suis d'accord avec une structure qui fait toutes ces opérations en temps , où est la longueur de la plus longue chaîne de la structure. Dans le cas de la fusion, je voudrais un temps d'exécution , où est pour la première et le pour la deuxième structure.o ( n ) n o ( n 1 + n 2 ) n 1 n n 2 nO(1)o(n)no(n1+n2)n1nn2n

Une exigence supplémentaire est que la structure soit immuable, ou du moins que les opérations ci-dessus renvoient de «nouvelles» structures telles que les pointeurs vers les anciennes fonctionnent toujours comme auparavant.

Une note sur l'amortissement: c'est bien, mais il faut faire attention à la persistance. Comme je réutilise les anciennes structures tout le temps, je serai en difficulté si je frappe le pire des cas avec un ensemble particulier d'opérations sur la même structure (donc en ignorant les nouvelles structures qu'il crée).

J'aimerais utiliser une telle structure dans un algorithme d'analyse sur lequel je travaille; la structure ci-dessus contiendrait l'anticipation dont j'ai besoin pour l'algorithme.

J'ai déjà envisagé d'utiliser un trie , mais le principal problème est que je ne sais pas comment fusionner les essais efficacement. Si l'ensemble de chaînes pour se Add-Prefix-Setcompose uniquement de chaînes à un seul caractère, vous pouvez stocker ces ensembles dans une pile, ce qui vous donnera des temps d'exécution pour les trois premières opérations. Cependant, cette approche ne fonctionne pas non plus pour la fusion.O(1)

Enfin, notez que je ne suis pas intéressé par les facteurs: c'est constant pour tout ce qui m'importe.|Σ|

Alex ten Brink
la source
Les chaînes sont-elles uniquement construites par l'opération Add-Prefix-Setou commencez-vous par un ensemble arbitraire de chaînes?
Joe
2
Supposons que , avant une opération de fusion, il y a une chaîne de longueur à la fois et . Comment pourriez-vous éventuellement détecter si cette chaîne est un doublon en temps ? S T o ( n 1 + n 2 )n1=n2STo(n1+n2)
Joe
Vous commencez avec un ensemble contenant une chaîne à un seul caractère, mais une chaîne vide convient également (vous pouvez le faire Add-Prefix-Set)
Alex ten Brink
@Joe: c'est une bonne question - je commence à être convaincu que l'opération de fusion rompt à peu près toutes les chances d'obtenir une telle structure ...
Alex ten Brink
Si vous utilisez votre représentation "pile d'ensembles", vous pouvez fusionner deux piles en min (n1,n2)
Joe

Réponses:

5

J'ai réfléchi pendant un certain temps, mais je n'ai pas trouvé le problème de faire toutes vos opérations de la manière la plus stupide possible dans une structure DAG de type trie:

Ajouter un ensemble de préfixes

Créer une structure arborescente de chaînes de . Connectez chaque nœud feuille à la racine de l'ancien trie.T

Complexité: O(|T|)

Fusionner

Unir les racines de deux structures: faire de tous les enfants des nœuds du deuxième enfant racine du premier nœud. Vous pouvez maintenant avoir plusieurs bords marqués avec le même caractère provenant du même nœud.

Complexité: O(1)

Mise à jour paresseuse de la racine

  1. Pour chaque caractère unir tous les enfants de la racine accessibles par les bords marqués de ce caractère. ( + O amorti ( 1 ) pour chaque bord ajouté)O(|Σ|)O(1)
  2. O(1)

Get-prefixes

Mise à jour paresseuse de la racine. Maintenant, trouvez tous les enfants de la racine et signalez un ensemble de lettres sur les bords qui vont vers eux.

O(|Σ|)

Supprimer les préfixes

Mise à jour paresseuse de la racine. Unissez tous les enfants de la racine et définissez le pointeur racine sur le résultat de cette unité. Mise à jour paresseuse de la nouvelle racine.

O(|Σ|)

Persistance

O(1)O(|Σ|)O(logN)N

maksay
la source