作为一个起点,我建议以下算法是正确的,没有评论其运行时间的最优化:
- 构建一个接受第一个字符串循环的DFA(让我们称它为“左”),例如
(ABC)*(e|A|AB)
其中e
是接受空字符串的正则表达式。
- 类似地构造一个接受第二个字符串循环的DFA(称为“right”)。
现在我们构造一个NFA(称之为N表示“不确定性”)为您的交织语言如下:
- 国N分别对状态(L,R),其中L为L的状态和r是R的状态
- 有一个从在N-标记为C当且仅当以下两个条件之一(L,R)至(L“ R”)的边缘成立:
- 有一个边缘从l到l'在L中标记为c并且r = r',或者从r到r'la存在边缘在R和l = 1'中定位c。
- (L,R)是最后的N IFF升是在最终的L和R是最终在R.
- N的开始状态为(L,R)其中,l是L的开始状态且r是R的起始状态
然后我认为ñ接受您的交织语言:通过该NFA接受路径对应于任一“L”或“R”的分配的选择,每个字符以及标记为“L”的字符通过L的接受路径以及标记为“R”的字符通过R的接受路径。
只是为了好玩,我已经把这个想法的一个非常小的实现放在了Haskell中,没有试图花大量的时间来做优化或类似的事情。 (因此,没有最小化的DFA,或试图memoize的状态转换,或类似的东西)。它的工作好了你的榜样,至少:
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)
下面是一些例子运行在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)
您能否以更一般的术语解释您的解决方案?我试图理解我将如何制作一张表,以便像存在许多DP问题那样存储此问题的较小实例。 – jojo
@jojo这不是一个基于动态编程的解决方案。试图将它理解为动态编程是一个错误。我不确定“更通用的术语”的含义是什么 - 也许你可以指出一个不清楚的具体说明。 –
哦,我明白了。我正在寻找针对我的问题的动态编程解决方案。我真的只是想了解我将如何解决这个问题,所以即使psuedocode是有帮助的。我主要关心的是,我将如何确定使用哪些前缀来创建第三个字符串。 – jojo