字符串匹配算法

旧时模样 提交于 2019-12-02 23:37:43

问题大致含义:给出两个字符串s1、s2,判断s2是否为s1的子串。


普通匹配算法:

  • 用两个下标i,j来记录s1和s2当前元素的下标,初始值都为0; 还需要一个index记录s1匹配的起始坐标。
  • 结束条件:i=len(s1)  or  j=len(s2) ;  如果j=len(s2),那么匹配成功;  否则,匹配失败。
  • 迭代过程: s1[i]=s2[j],则i++ , j++;   如果不相等,那么j=0;index++; i=index;

 

时间复杂度:O(mn),m,n是两个串的长度。

 1 /* 字符串下标始于 0 */
 2 int NaiveStringSearch(string S, string P)
 3 {
 4     int i = 0;    // S 的下标
 5     int j = 0;    // P 的下标
 6     int s_len = S.size();
 7     int p_len = P.size();
 8 
 9     while (i < s_len && j < p_len)
10     {
11         if (S[i] == P[j])  // 若相等,都前进一步
12         {
13             i++;
14             j++;
15         }
16         else               // 不相等
17         {
18             i = i - j + 1;
19             j = 0;
20         }
21     }
22 
23     if (j == p_len)        // 匹配成功
24         return i - j;
25 
26     return -1;
27 }

KMP算法:

算法核心:当算法匹配成功的时候与普通的字符串匹配算法并没有太大的不同,都是比较下一个元素;但是在匹配失败的时候,利用某些信息来减少一些不必要的匹配。

 

next数组:

  • next数组是KMP的核心数据结构,通过next的特性能够减少不必要的匹配。
  • next数组的含义:对于一个字符串S,其长度为len(S),那么next的下标范围为:0~len(S)-1,对于i∈[0,len(S)],next[i]:使子串S[0:i]中形成前缀S[0:k]后缀S[i-k:i]相同最大k值(k一定小于i,当不能匹配,取-1)。

 

用一个例子来解释next[i]:   S="ababaab"  len(S)=7              (下面所用的符号与上面说明对应)

i 0 1 2 3 4 5 6
next[i] -1 -1 0 1 2 3 -1
  • i=0时,默认为-1;(只有一个元素,S[0])
  • i=1时,S[0:1]="ab",next[i]=-1,因为k=0时,S[0:0]="a",S[1:1]="b",不匹配,k必须小于i。
  • i=2时,S[0:2]="aba",next[i]=0,因为k=0时,S[0:0]="a",S[2:2]=“a”,匹配;    k=1时,S[0:1]="ab",S[1:2]="ba",不匹配。
  • i=3时,S[0:3]="abab",next[i]=1。 
  • i=4,5,6::略

 

  • next数组的意义:对于S,给出了所有S[0:i]的子串最长的前缀与后缀相同的位置。(字符串匹配的目的是实现所有的相同,所有相同必定是部分相同的子集,因此能够跳入到下一个可能进行匹配的位置)

 

KMP算法:

  • 注意:在匹配失败的时候如何利用next数组,以及i,j如何变化。

 

S1="abababaabc"    S2="ababaab",判断S2是否为S1的子串。

  • 建立S2的next数组                   (为什么要建立S2(匹配串)的next数组?    因为它需要主动匹配,涉及到前缀和后缀的等价转换,不同串之间的等价转换
  • 用i指向S1欲匹配的字符,用j指向S2已匹配的字符;i=0,j=-1; 
  • 用S1[i]与S2[j+1]进行匹配:
    • i∈[0,4],是匹配成功的,这时候只需要i++,j++; 最后到达i=5,j=4;(匹配成功情况
    • 因为S1[i]="b",S2[j+1]="a",因此不匹配;    关键点来了,那么用什么元素进行再次比较呢? (图1) (匹配失败情况)     S1[0:4]=S2[0:4],  根据next数组,next[j]=2(说明S2[0:2]=S2[2:4] ) S2[2:4]=S1[2:4],   经过等价代换, 同时令j=next[j]  ,可以发现:S2[0:2]=S1[2:4];    在此时再比较S1[i]与S2[j+1],看是否相等(i=5,j=2);这时候发现相等了,因此按照成功时候进行匹配。

                                        图1

  

                                        图2

算法步骤综述:(用数学关系完整地阐述一遍)  S1:被匹配串, S2:匹配串

  • 用i指向S1欲匹配的字符,用j指向S2已匹配的字符;i=0,j=-1;     建立S2的next数组。
  • 循环结束条件:当j=len(S2)-1 or i=len(S1)  ;   如果j=len(S2)-1,那么匹配成功;  否则,匹配失败
  • 循环过程:
    • 如果S1[i]=S2[j+1],那么匹配成功,i++,j++;
    • 如果S1[i]!=S2[j+1],那么匹配失败,j=next[j],再次判断S1[i]与S2[j+1]的关系;(继续循环)      如果j=-1,并且匹配失败,那么i++;
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!