我编写了一个优化的搜索工具,首先在句子中查找固定的字符短语 - 认为它很简单“句子是否包含特定的字符序列”。结果将是一组已找到的句子,可以在第二阶段进一步搜索。
对于第二阶段,我喜欢使用正则表达式搜索以方便起见。但我需要在第一阶段首先预先选择项目,我不能简单地得到所有句子 - 我需要在第一阶段使用的API要求我搜索至少一个匹配字符的短语。所以没有办法解决这个问题。
现在,用户只会输入一个正则表达式,我的软件需要首先确定它是否可以执行第一阶段搜索。如果用户输入含糊不清的内容,我会告诉用户更改他的正则表达式。
我需要算法来确定我可以用于第一阶段搜索的所有子串。
以下是预期结果的一些示例:
这些是简单的例子。但是由于正则表达式会变得非常复杂,我想知道我是否可以构建一个正则表达式或其他测试,告诉我是否有可用的结果。 我还可以将正则表达式语法限制为更常见的情况,如果这使得测试更简单,例如没有递归或任何可能有帮助的东西。
答案 0 :(得分:1)
这是一个使用Haskell的例子。该算法应该很容易转移到另一种语言。
只是一些样板导入;
import Data.Maybe
import Data.List
import Data.Function
这是表示正则表达式的数据类型。您必须自己解析它或使用库:
data Regex
= Concat Regex Regex -- e.g. /ab/
| Alt Regex Regex -- e.g. /a|b/
| Single Char -- e.g. /a/
| Star Regex -- e.g. /a*/
| CharClass [(Char,Char)] -- a list of ranges. for non-range (e.g. [a]) just use the same char twice
以下是算法:
regexMustMatch (Single x) = [Just x] -- has to match the character
regexMustMatch (Alt _ _) = [Nothing] -- doesn't need to match one thing (you could actually check for equality here, so something like /a|a/ would work)
regexMustMatch (Star _) = [Nothing] -- doesn't need to match one thing
regexMustMatch (CharClass ((a,b):[])) | a == b = [Just a] -- char class must match if it only has one character
regexMustMatch (CharClass _) = [Nothing] -- otherwise doesn't need to match one thing
regexMustMatch (Concat x y) = (regexMustMatch x) ++ (regexMustMatch y) -- must match both parts in sequence
使结果可用的一些方法:
selectAll = map (concatMap (return . fromJust)) .
filter (isJust . head) .
groupBy ((==) `on` isJust)
selectLongest x = case selectAll x of
[] -> ""
xs -> maximumBy (compare `on` length) xs
还有一些例子:
main = do
-- your tests
-- /ab/
print . selectAll . regexMustMatch $ (Single 'a' `Concat` Single 'b')
-- /a|b/
print . selectAll . regexMustMatch $ (Single 'a' `Alt` Single 'b')
-- /[ab]/
print . selectAll . regexMustMatch $ (CharClass [('a','a'),('b','b')])
-- /[ab]c/
print . selectAll . regexMustMatch $ ((Single 'a' `Alt` Single 'b') `Concat` Single 'c')
-- a few more
-- /[a]/
print . selectAll . regexMustMatch $ (CharClass [('a','a')])
-- /ab*c/
print . selectAll . regexMustMatch $ (Single 'a' `Concat` Star (Single 'b') `Concat` Single 'c')
-- /s(ab*)(cd)/ - these aren't capturing parens, just grouping to test associativity
print . selectAll . regexMustMatch $ (Single 's' `Concat` (Single 'a' `Concat` Star (Single 'b')) `Concat` (Single 'c' `Concat` Single 'd'))
输出:
["ab"] -- /ab/
[] -- /a|b/
[] -- /[ab]/
["c"] -- /[ab]c/
["a"] -- /[a]/
["a","c"] -- /ab*c/
["sa","cd"] -- /s(ab*)(cd)/
可以改进的主要区域是交替算法。
如果我们有正则表达式/a*bc*|d*be*/
,那么b
需要匹配,但这不会选择它。
编辑:这是一种改进的替代算法:
regexMustMatch (Alt x y)
| x' == y' = x'
| otherwise = start ++ [Nothing] ++ common ++ [Nothing] ++ end
where
x' = regexMustMatch x
y' = regexMustMatch y
start = map fst $ takeWhile (uncurry (==)) (zip x' y')
end = map fst $ reverse $ takeWhile (uncurry (==)) (zip (reverse (drop (length start) x')) (reverse (drop (length start) y')))
dropEnds = drop (length start) . reverse . drop (length end) . reverse
common = intercalate [Nothing] $ map (map Just) (selectAll (dropEnds x') `intersect` selectAll (dropEnds y'))
改进后的更多测试:
/a*bc*|d*be*/ == b
/s(abc*|abe*)/ == sab
/s(a*bc*|d*be*)/ == s, b
/sa*b|b*/ == s
/(abc*|abe*)s/ == ab, s
/(a*bc*|d*be*)s/ == b, s
/(a*b|b*)s/ == s
/s(ab|b)e/ == s, be
/s(ba|b)e/ == sb, e
/s(b|b)e/ == sbe
/s(ac*b|ac*b)e/ == sa, be