• 字符串算法—正则表达式


    1. 前文回顾

      在字符串算法—字符串搜索中,我们实现了从一堆字符中搜索某个字符串的高效算法。

      但如果要在一堆字符中找具有某些规律的字符串(要找的字符串是不确定的,但有规律),该如何设计算法?

      本文将介绍NFA算法来解决此问题。

    2. 正则表达式

      首先,有规律的字符串是长什么样的呢?例如:ABBBBBBBBBA。

      我们需要用一种方式来表达这个规律。于是我们就有了正则表达式。

      ABBBBBBBBBA可以表达为AB*A。其中B*表示有N个B,N可以为0。

      然后以下均是正则表达式:

      

      

      [A-E]+表示:(A|B|C|D|E)(A|B|C|D|E)*

      

      有了这些表达式后,我们就能让程序读懂字符串的规律,然后就是让它找出含有这规律的字符串在哪了。

      

    3. NFA算法

      本算法需要有向图基础,如果不了解有向图的,可以先看一下图表算法—有向图

      从例子中开始了解此算法:

      现有正则表达式: ( ( A * B | A C ) D )。

      我们通过某种构建方法(此方法稍后介绍),把这个正则表达式变成一个有向图:

      

      我们将逐个逐个符号来读这个表达式。图中的0,1,2,3,...,10,11是阶段,当我们抵达第11阶段,则说明找到符合这表达式的字符串。

      图中的线有两种颜色,红色的是过渡线,可以不读阶段里面的内容,直接跳到下一个指定阶段;蓝色是正常线,要先读阶段里面的内容,再改变阶段。

      例如:第4阶段只能去第5阶段;第1阶段可以去第2阶段、第3阶段、第4阶段、第6阶段。

      现在我们来看一下AAAABD是否符合这个表达式:

      第一个字符为A,我们从第0阶段开始:

      

      0,1阶段都是括弧,故符合。现在1可以去2、3、4和6,且2和6都是A,与第一个字符匹配,故两个阶段都去:

      

      第二个字符为A,现在可以去2,4,7(其中去2的路线为2-3-2,去4的路线为2-3-4),但匹配第二个字符的只有第2阶段,故去第2阶段:

      

      第三个字符为A,现在可以去2,4(其中去2的路线为2-3-2,去4的路线为2-3-4),但匹配第三个字符的只有第2阶段,故去第2阶段:

      

      第四个字符为A,现在可以去2,4(其中去2的路线为2-3-2,去4的路线为2-3-4),但匹配第四个字符的只有第2阶段,故去第2阶段:

      

      第五个字符为B,现在可以去2,4(其中去2的路线为2-3-2,去4的路线为2-3-4),但匹配第五个字符的只有第4阶段,故去第4阶段:

      

      第六个字符为D,现在可以去9(其中去9的路线为4-5-8-9,由于5,8都是符号,不能与字符比较,故无视它们),第9阶段匹配第五个字符,故去第9阶段:

     

      没有第七个字符,且第9阶段可以直接抵达第11阶段,故这个字符串符合此表达式。

      如果要从这个字符串中,找出有多少个符合此表达式的字符串,则经过上面的寻找,我们找到了第一个:AAAABD。

      接下来寻找第二个,从第二个字符开始第0阶段,如果能顺利抵达第11阶段,说明AAABD也符合;否则,AAABD不符合。

      然后第三个,从第三个字符开始第0阶段,如此类推,直到所有字符都开始过第0阶段为止,然后统计有多少个字符串符合,且记住它们的位置。

      如此类推,我们可以找出整段字符中符合这个表达式的所有字符串,从而解决一开始提出的问题。

      接下来就要看如何把这个正则表达式转化为有向图了:

      首先,如果某阶段里含有的不是符号,而是字符,则可以直接去下一个阶段:

      

      然后,如果遇到的是符号 * ,则分两种情况:

      1. *所处的阶段的前一阶段是字符:

      

      则加3条方向(不用担心重复加方向,因为代码中如果出现重复的,直接覆盖)。

      2. *所处的阶段的前一阶段是右括弧:

      

      也是加3条方向:与前一个左括弧加一个互相的方向,我们会用一个数字lp来记住前一个左括弧。

      回到我们的这个例子,给*阶段添加方向:(符号用的线都是过渡线

      

      如果遇到的是左括弧或者是 | 则要把它们按顺序加到栈中,顺便给左括弧加一个指向下一个阶段的方向:(新建栈A)

      

      如果遇到的是右括弧,则给它加一个指向下一个阶段的方向,且栈A输出数个元素,直到见到左括弧为止:

      

      然后栈A输出一个元素(先进先出):第5阶段的 | ,新建一个整数int or=5来记住它,并且给它加一个指向目前所处位置的右括弧处:

      

      然后栈A输出一个元素:第1阶段的(,给它加一个指向or+1位置的方向:(如果有多个or,则分别指向各个or+1位置)

      

      然后遇见的是第10阶段的),给它加一个指向下一个阶段的方向,且栈A输出数个元素,直到见到左括弧为止:

      

      然后栈A输出一个元素:第0阶段的(,中途没见到 |, 因此不作操作:

      

      然后去到第11阶段,构建完毕。

    代码实现:

      根据正则表达式构建有向图:

      

      图中红色箭头处,它默认了栈下一个吐出来的是左括号,但如果吐出来的是 | ,则会出错,这里应该做一个针对连续吐出多个 | 的处理(加个判断)。

      这段代码只处理了符号,应该添加代码给字符所处的阶段指向下一个阶段。

      使用这个有向图来寻找字符串:

      

      

  • 相关阅读:
    【Prometheus学习笔记】主机监控 -node_exporter
    【Django学习笔记】-环境搭建
    【Python学习笔记】-虚拟环境virtualenv
    对象存储服务-Minio
    网络流各算法超详细带源码解析
    做题记录节选
    日常
    板刷NOI
    题解 宝石
    题解 矩阵游戏
  • 原文地址:https://www.cnblogs.com/mcomco/p/10432071.html
Copyright © 2020-2023  润新知