En tant que point de départ, je propose à la L'algorithme suivant est correct, sans aucun commentaire sur l'optimisation de son exécution:
- Construire un DFA (appelons-le L pour "left") qui accepte les boucles de la première chaîne, par ex.
(ABC)*(e|A|AB)
où e
est l'expression régulière qui accepte la chaîne vide.
- De même, construisez un DFA (appelez-le R pour "right") qui accepte les boucles de la deuxième chaîne.
Nous construisons maintenant un NFA (appeler N pour « non déterministes ») pour votre langue désentrelacement comme suit:
- Etats N sont des paires d'états (l, r) où l est un état de L et r est un état de R.
- Il y a un bord à partir de (l, r) (l 'r') marqué dans c N ssi une des deux conditions suivantes est vérifiée:
- Il y a un bord de l à l 'marqué c dans L et r = r', OU
- Il y a un bord de r à r 'la notée c dans R et l = l '.
- (l, r) est définitive en N ssi l est finale en L et r est définitive dans R.
- l'état initial de N est (l, r) où l est l'état de départ de L et r est l'état initial de R.
Je pense que N accepte votre langue désentrelacement: accepter des chemins à travers cette NFA correspondent à un choix d'affectation de « l » ou « R » pour chaque caractère avec un chemin d'acceptation à travers L pour les caractères étiquetés "L" et un chemin d'acceptation à travers R pour les caractères marqués "R". Juste pour le plaisir, j'ai mis en place une très petite implémentation de cette idée dans Haskell, sans essayer de passer beaucoup de temps à faire de l'optimisation ou quoi que ce soit de ce genre. (Donc pas de minimisation de DFA, ou d'essayer de memoize transitions d'état, ou quelque chose comme ça.) Il fonctionne bien pour votre exemple, au moins:
import Data.List
type Position a = ([a], [a])
type DFAState a = (Position a, Position a)
type NFAState a = [DFAState a]
initialize :: [a] -> [a] -> NFAState a
initialize xs ys = [(([], xs), ([], ys))]
stepPosition :: Eq a => a -> Position a -> [Position a]
stepPosition x ([], [] ) = []
stepPosition x (bs, [] ) = stepPosition x ([], reverse bs)
stepPosition x (bs, e:es) = [(e:bs, es) | e == x]
ordNub :: Ord a => [a] -> [a]
ordNub = concatMap (take 1) . group . sort
stepDFA :: Ord a => a -> DFAState a -> [DFAState a]
stepDFA x (l, r) = ordNub
$ [(l', r) | l' <- stepPosition x l]
++ [(l, r') | r' <- stepPosition x r]
stepNFA :: Ord a => a -> NFAState a -> NFAState a
stepNFA x ss = concatMap (stepDFA x) ss
evalNFA :: Ord a => [a] -> NFAState a -> Bool
evalNFA _ [] = False
evalNFA [] ss = True
evalNFA (c:cs) ss = evalNFA cs (stepNFA c ss)
top :: Ord a => [a] -> [a] -> [a] -> Bool
top ls rs cs = evalNFA cs (initialize ls rs)
Voici quelques exemples d'exécution dans ghci:
*Main> :set +s
*Main> top "ABC" "ADC" "ABCADCABADCCA"
True
(0.00 secs, 0 bytes)
*Main> top "ABC" "ADC" "ABCADCABADCCADABCC"
True
(0.01 secs, 0 bytes)
*Main> top "ABC" "ADC" "ABCADCABADCCADADCC"
False
(0.00 secs, 0 bytes)
Pourriez-vous expliquer votre solution en termes plus généraux?J'essaie de comprendre comment je pourrais faire une table qui pourrait stocker les plus petites instances de ce problème comme vous le faites pour de nombreux problèmes de DP. – jojo
@jojo Cette solution n'est pas basée sur une programmation dynamique. Essayer de le comprendre comme une programmation dynamique est une erreur. Je ne suis pas sûr de ce que «termes plus généraux» signifie - peut-être que vous pouvez pointer vers une partie spécifique de cette exposition qui n'est pas claire. –
Oh je vois. Je cherchais une solution de programmation dynamique à mon problème. J'essaie juste de comprendre comment j'aborderais ce problème, même si psuedocode est utile. Ma principale préoccupation est de savoir comment je déterminerais quels préfixes ont été utilisés pour créer la troisième chaîne. – jojo