zoukankan      html  css  js  c++  java
  • 字符串匹配(1)

    我接触字符串匹配算法大概有一年半了(从八年级的四月份开始),但是目前为止,这些字符串匹配算法对我来说概念还是比较模糊,特此写一个专题,总结一下。

    题目

    对于一个文本T,希望能找到与模式P相匹配的 T的子串T'的位置。其中,T的长度记为n,P的长度记为m,且总保证n>=m

    image

    朴素算法

    其真正的时间复杂度应该为Θ((n-m+1)m)。

    这种方法的好处在于易于理解,当m比较大时,此方法的优势非常大。

    朴素算法的具体内容不做介绍。

    Rabin-Karp算法

    这种算法的时间复杂度不稳定。最差时和朴素算法一样O((n-m+1)m)。

    对于长度为m的字符串s,可以建立一个映射hash(s)(hash表),得到一个值。此操作称为预处理。

    比如:

    // ASCII a = 97, b = 98, r = 114.

    hash("abr") = (97 × 1012) + (98 × 1011) + (114 × 1010) = 999,509

    参见维基百科

    这样,把每一个长度为m的T的子串都进行hash(T')运算,并与hash(P)比较,若hash(T')==hash(P),则进一步比较T'与P即可。

    利用有限自动机进行匹配

     
    请参见算法导论583页,或维(伪)基(娘)百科

    此算法的预处理时间为O(|∑|m),匹配时间为Θ(n)。

    有限自动机的概念:

    1. 有限自动机,aka DFA,aka Deterministic Finite Automaton, aka 确定有限状态自动机,可以使用一个五元组表示image

    其中,Q为状态集,∑为字母表,δ是转移函数(image),q0 是开始状态,F是终态集(接受状态集合)

    2. ∑*(其中的*)被称作Kleensche Hülle,表示由∑所产生的所有可能的字符串的集合。比如{"ab", "c"}* = {ε, "ab", "c", "abab", "abc", "cab", "cc", "ababab", "ababc", "abcab", "abcc", "cabab", "cabc", "ccab", "ccc", ...}

    3. 如果有限自动机在状态q时读入了字符a,则它从状态a变为状态δ(q,a)。每当当前状态q属于F,就说自动机M接受了迄今为止读入的字符串。

    没有被接受的输入称为被拒绝的输入。

    4. ϕ称为终态函数,它是从∑*到Q的函数,满足ϕ(w)是M在扫描字符串w后终止的状态。因此,当且仅当ϕ(w)属于F时,M接受字符串w。

    ϕ的定义为:ϕ(ε)=q0, ϕ(wa)=δ(ϕ(w),a)

    字符串匹配自动机的概念:

    1. 定义一个辅助函数σ,表示一个从∑*到{0,1,2,…,m}的映射。

    即:σ(x)=max{k: Pk后缀于k}

    例如:对于P=ab,有σ(ε)=0, σ(ccaca)=1, σ(ccab)=2

    2. 字符串匹配自动机的定义如下:

    a) 状态集合Q={0,1,…,m}。

    b) 开始状态q0是0状态,并且只有装填m是唯一被接受的状态。

    c) 对于任意的状态q和字符a,状态转移函数定义为δ(q,a)=σ(Pqa)

    3. 易证:ϕ(Ti)=σ(Ti)

    ねね~上面这些学会了话,预处理就很容易写出来了。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <string>
     4 #include <cstring>
     5 #include <algorithm>
     6 
     7 using namespace std;
     8 const int SigmaSize=3,MAXM=1000;
     9 const string Sigma="abc";
    10 string T,P;
    11 int State[MAXM][SigmaSize+1]={0};
    12 void init_delta(){
    13     for (int i=0;i<=P.size();i++){  /*注意此处要写<=*/ 
    14         for (int j=0;j<SigmaSize;j++){
    15             string patt,temp;
    16             temp=P.substr(0,i)+Sigma[j];
    17             int k=min(i+1,(int)P.size());      /*i+1的大小不能超过m*/ 
    18             patt=P.substr(0,i+1);
    19             if (temp.size()>patt.size()) temp=temp.substr(1,temp.size()-1);
    20             cout<<i<<":"<<endl<<temp<<endl<<patt<<endl;
    21             while (temp!=patt&&k!=0){
    22                 k--;
    23                 temp=temp.substr(1,temp.size()-1);
    24                 patt=patt.substr(0,patt.size()-1);
    25             }
    26             State[i][j]=k;
    27         }
    28     }
    29 }
    30             
    31 
    32 int main(){
    33     freopen("String_DFA.in","r",stdin);
    34     cin>>T>>P;
    35     init_delta();
    36     return 0;
    37 }
    预处理部分

     然而,毕竟我技不如人,别人写的代码怎么看我都觉得惊叹不已。放上别人的代码对比一下吧。

     1 bool cmp(char p[],char j,int k,int l)
     2 {
     3      int ret = (p[k-1]==j);
     4      if( ret == 0 ) return true;
     5      for(int i = 0;i<k-1;++i)
     6         if(p[l-k+1+i] == p[i]) ++ret;
     7      return !(ret == k);
     8 }
     9 int transition(char p[])
    10 {
    11     int m = strlen(p);
    12     for(int i=0;i<=m;++i)
    13     {
    14         for(int j=0;j<=255;++j)//这是精华所在啊!以为ascii中刚好256个字符所以这里代表了整个字符。
    15         //所以不管什么字符都不要处理。但是缺点是占用大量的空间。 
    16         {
    17             int k = ((m+1<i+2)?m+1:i+2);
    18             int l = i;
    19             do
    20             {
    21                 k = k-1;
    22             }while(k>0&&cmp(p,j,k,l));
    23             t[i][j] = k;
    24         }
    25     }
    26     return m;
    27 }
    别人家的代码
     1 void fsm(char s[],char p[])
     2 {  
     3     int n = strlen(s);
     4     int m = transition(p);
     5     int q = 0;
     6     for(int i = 0;i<n; ++i)
     7     {
     8         q = t[q][s[i]];
     9         if( q == m )
    10         {
    11             printf("Pattern occur with %d 
    ",i+1-m);
    12         }
    13     }
    14     putchar('
    ');
    15 }
    别人家的主程序
    参考资料:
    1. 刘汝佳. 算法竞赛入门经典(第二版). 北京:清华大学出版社,2009
    2. Thomas H. Cormen等. 算法导论(第三版). 北京:机械工业出版社,2013
  • 相关阅读:
    社会影响力入门:打造有所作为的公司
    JSP中的include有哪些?有什么差别?
    关于程序猿怎样降低程序Bug的若干建议
    linux文件打开模式
    IntelliJ IDEA 问题总结之中的一个 —— jar包、assets、maven、git
    linux下改动内核參数进行Tcp性能调优 -- 高并发
    思科模拟器配置三层交换机局域网
    MFC调试小技巧
    JAVA基础编程50题(10-12题)具体解释
    加速度传感器的原理和应用-手机翻转、失重检测、运动检测、位置识别
  • 原文地址:https://www.cnblogs.com/Chuckqgz/p/5671479.html
Copyright © 2011-2022 走看看