Viaky
Hope,is there.

子串匹配问题。

朴素算法:将子串中每一个字符与主串进行对比,子串对比完说明在主串中有此子串,输出当前比较的位数减去子串的长度,即子串在主串中出现的位数。若主串比较完、说明主串中没有查找的子串。输出零。

既然是朴素算法,效率低是必然的。KMP算法解决了朴素算法中重复计算的弊端。利用培训教程里的例子:

主串:G A C G A T A T A A G C C A G

子串:A T AT A C G

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

G

A

C

G

A

T

A

T

A

A

G

C

C

A

G

 

A

T

A

T

A

C

G

 

     

A

T

A

T

A

C

G

【上面一行博客里格式对不齐,将最后一行的首字母对齐到7那里在看】

第一次匹配中比较到第十位不匹配后,朴素算法应是继续从主串第六位开始,与子串第一位开始比较。但分析子串可以看出,t[7]t[8]=t[9]t[10],此时可以将子串“滑动到”主串的第七位,将主串第九位与子串第三位进行比较,以此来减少比较的次数。

由此,我们需要一个数组来记录子串中相同的“子子串”的位置。例如例子中的“AT”。将此数组设为NEXT,NEXT数组的值如下表。

1

2

3

4

5

6

7

A

T

A

T

A

C

G

0

1

1

2

3

4

1

注释:NEXT[1]=0.//必须。

   NEXT[2]=1,当子串与主串比较到子串的第二位不匹配时,转到子串的第1位进行比较。因为第二位之前没有与开头字母相匹配的子子串。

      NEXT[3]=1,同上。

      NEXT[4]=2,当匹配到第四位时若不匹配可从第三位起与主串的下一位继续比较。因为第四位前存在A=A。即t[1]=t[3]。下一次比较前已经知道t[1]与主串的下一位相同,省去了再次比较的麻烦。

      NEXT[5]=3,t[1]t[2]=t[3]t[4],下次可从第三位开始比较。

     依此类推。

 

关于NEXT数组的创建,则相当于主串与子串的不断比较过程。执行过程的原理是相同的。

program kmp;

  var

    i,j,n:longint;

    s,t:ansistring;

    next:array[1..1000001]of longint;

  procedure getnext(t:ansistring);//Next数组的创建

    var

      j,k:integer;

    begin

      j:=1; k:=0;//初始化,j为比较的子串的下标,k为寻找的相同的子子串的下标

      while j<length(t) do //如果子串未比较完,进入循环查找子子串

        if (k=0)or(t[j]=t[k]) then //k=0即第一位,或者子子串在子串中找到匹配,则继续往后比较

          begin

            inc(j);

            inc(k);//两个下标都向后移一位。

            next[j]:=k;//将匹配完后的值赋值给Next数组。

          end

         else k:=next[k];//如果不匹配,字字串的下标转到需要比较的那一位,避免重复查找。

    end;

  function index(s:ansistring;t:ansistring):longint; //计算子串出现的位数

    var

      i,j:longint;

    begin

      getnext(t);//Next数组取值。

      index:=0;//若没有子串的匹配则输出0.

      i:=1; j:=1;//i是主串的下标,j为子串的下标。

      while (i<=length(s))and(j<=length(t)) do//主串与子串都未比较完就继续查找。

        begin

          if (j=0)or(s[i]=t[j]) then //与getnext的原理相同。

            begin

              inc(i);

              inc(j);

            end

          else j:=next[j];

          if j>length(t) then index:=i-length(t);//如果子串比较完,则子串在主串中有匹配,此时输出的应是当前位减去子串的长度,即子串在主串出现的位数。

        end;

     end;

  begin

    readln(s);  //输入主串

    readln(t);  //输入子串

    writeln(index(s,t)); //index函数计算子串在主串中出现的位数并输出。

  end.

 

最坏的时间复杂度:O(M+N),M和N分别是主串和子串的长度。

posted on 2011-03-16 15:08  Viaky  阅读(202)  评论(0编辑  收藏  举报