在长度为n的数组T[n]中查找一个长度为m的数组P[m],如果用朴素字符串匹配方法要用O(mn)的时间,用自动机匹配要O(n)的时间,但一般的自动机要O(ml)的时间(l为字符集的宽度),而KMP只要O(m)的预处理时间。

其实最早接触字符串匹配自动机应该是在数字电路中的序列检测器那时候,序列检测器是用硬件区实现一个个状态的转换,这里和那儿是一个原理主程序维持一个状态量mode是在读入某个字符后的匹配长度,它随着继续读入字符而不断变化,而变化的函数可以由模式P[m]和字符集去确定,确定这个函数的时间可以为O(ml)(根据《算法导论》,但是我的程序都是O(m^3 * l)的,不知道他是怎么实现的),以杭电1686题为例:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<memory.h>
  4 
  5 #define MAXM 1000005
  6 #define MAXN 10005
  7 #define MAXW 26
  8 
  9 char str[MAXM];
 10 char key[MAXN];
 11 int mode[MAXN][MAXW];
 12 
 13 int FINITE_AUTOMATION_MATCHER();
 14 void COMPUTE_TRANSITION_FUNCTION();
 15 
 16 int main()
 17 {
 18     //freopen("Sample Input.txt","r",stdin);
 19     int T;
 20     scanf("%d",&T);
 21     
 22     while(T--)
 23     {
 24         scanf("%s%s",key,str);
 25         printf("%d\n",FINITE_AUTOMATION_MATCHER());
 26     }
 27 
 28     return 0;
 29 }
 30 
 31 int FINITE_AUTOMATION_MATCHER()
 32 {
 33     int len1 = strlen(str);
 34     int len2 = strlen(key);
 35     int cnt = 0;
 36     int cur_mode = 0;
 37 
 38     memset(mode,0,sizeof(mode));
 39     COMPUTE_TRANSITION_FUNCTION();
 40 
 41     for(int i = 0;i < len1;i++)
 42     {
 43         cur_mode = mode[cur_mode][str[i] - 'A'];
 44         cnt = cur_mode == len2 ? cnt + 1 : cnt;
 45     }
 46 
 47     return cnt;
 48 }
 49 
 50 
 51 void COMPUTE_TRANSITION_FUNCTION()            //四层循环求转换函数,O(m^3 * l)的复杂度
 52 {
 53     int len = strlen(key);
 54 
 55     for(int i = 0;i <= len;i++)                                    
 56     {
 57         for(int j = 0;j < 26;j++)                    //写的有点仓促,很乱。。。。
 58         {
 59             int k = i + 1 > len ? len : i + 1;
 60             int flag = 0;
 61 
 62             for(;k > 0;k--)
 63             {
 64                 flag = 1;
 65 
 66                 if(key[k - 1] - 'A' != j)
 67                 {
 68                     continue;
 69                 }
 70 
 71                 for(int s = 0;s < k - 1 && flag;s++)
 72                 {
 73                     flag = key[s] == key[i - k + s + 1] ? 1 : 0;
 74                 }
 75 
 76                 if(flag)
 77                 {
 78                     break;
 79                 }
 80             }
 81             mode[i][j] = k;
 82         }
 83     }
 84 }
 85 
 86 
 87 
 88 
 89 
 90 普通的自动机由于转移函数耗时太长TL了。KMP算法类似于自动机它也维持一个暂时的匹配长度mode,在读入下一个字符时如果匹配不成功,那么就要减小mode,求减小的量(前缀函数)就是KMP的核心。前缀函数f[i]是满足是模式P[i]的真后缀和前缀的最大长度,算法中求前缀函数的过程就是模式自己和自己匹配的过程,f[i]就是在读入第i个字符时和模式P的最大匹配长度,因此求前缀函数的过程和匹配过程高度相似,下面还是以hdu1686题为例:
 91 
 92 #include<stdio.h>
 93 #include<string.h>
 94 #include<memory.h>
 95 
 96 #define MAXM 1000005
 97 #define MAXN 10005
 98 
 99 char str[MAXM];
100 char key[MAXN];
101 int mode[MAXN];
102 
103 int KMP_MATCHER();
104 void COMPUTE_PREFIX_FUNCTION();
105 
106 int main()
107 {
108     //freopen("Sample Input.txt","r",stdin);
109     int T;
110     scanf("%d",&T);
111     
112     while(T--)
113     {
114         scanf("%s%s",key,str);
115         printf("%d\n",KMP_MATCHER());
116     }
117 
118     return 0;
119 }
120 
121 int KMP_MATCHER()
122 {
123     int cnt = 0;
124     int cur_mode = 0;                    //相当于自动机中的状态
125     int len1 = strlen(str);
126     int len2 = strlen(key);
127 
128     memset(mode,0,sizeof(mode));
129     COMPUTE_PREFIX_FUNCTION();
130 
131     for(int i = 0;i < len1;i++)
132     {
133         while(cur_mode > 0 && key[cur_mode] != str[i])
134         {
135             cur_mode = mode[cur_mode];
136         }
137 
138         if(key[cur_mode] == str[i])
139         {
140             cur_mode++;
141         }
142 
143         if(cur_mode == len2)
144         {
145             cnt++;
146         }
147     }
148 
149     return cnt;
150 }
151 
152 void COMPUTE_PREFIX_FUNCTION()            //自己和自己匹配的过程
153 {
154     mode[1] = 0;
155 
156     int k = 0;                            //已匹配长度
157 
158     for(int i = 2;i <= strlen(key);i++)
159     {
160         while(k > 0 && key[k] != key[i - 1])
161         {
162             k = mode[k];
163         }
164 
165         if(key[k] == key[i - 1])
166         {
167             k++;
168         }
169 
170         mode[i] = k;
171     }
172 }
173 
174 //7738004    2013-03-11 19:14:48    Accepted    1686    671MS    1268K    1152 B    G++    超级旅行者

 

算法还是不够快,功力尚浅啊,还需修炼,看看有没有更快的算法。


参考文献:《算法导论》


 

 posted on 2013-03-11 21:33  莫扎特的代码  阅读(271)  评论(0编辑  收藏  举报