字符串匹配自动机
2013-03-11 21:33
281 查看
在长度为n的数组T
中查找一个长度为m的数组P[m],如果用朴素字符串匹配方法要用O(mn)的时间,用自动机匹配要O(n)的时间,但一般的自动机要O(ml)的时间(l为字符集的宽度),而KMP只要O(m)的预处理时间。
其实最早接触字符串匹配自动机应该是在数字电路中的序列检测器那时候,序列检测器是用硬件区实现一个个状态的转换,这里和那儿是一个原理。主程序维持一个状态量mode是在读入某个字符后的匹配长度,它随着继续读入字符而不断变化,而变化的函数可以由模式P[m]和字符集去确定,确定这个函数的时间可以为O(ml)(根据《算法导论》,但是我的程序都是O(m^3 * l)的,不知道他是怎么实现的),以杭电1686题为例:
普通的自动机由于转移函数耗时太长TL了。KMP算法类似于自动机它也维持一个暂时的匹配长度mode,在读入下一个字符时如果匹配不成功,那么就要减小mode,求减小的量(前缀函数)就是KMP的核心。前缀函数f[i]是满足是模式P[i]的真后缀和前缀的最大长度,算法中求前缀函数的过程就是模式自己和自己匹配的过程,f[i]就是在读入第i个字符时和模式P的最大匹配长度,因此求前缀函数的过程和匹配过程高度相似,下面还是以hdu1686题为例:#include<stdio.h>
#include<string.h>
#include<memory.h>
#define MAXM 1000005
#define MAXN 10005
char str[MAXM];
char key[MAXN];
int mode[MAXN];
int KMP_MATCHER();
void COMPUTE_PREFIX_FUNCTION();
int main()
{
//freopen("Sample Input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s%s",key,str);
printf("%d\n",KMP_MATCHER());
}
return 0;
}
int KMP_MATCHER()
{
int cnt = 0;
int cur_mode = 0; //相当于自动机中的状态
int len1 = strlen(str);
int len2 = strlen(key);
memset(mode,0,sizeof(mode));
COMPUTE_PREFIX_FUNCTION();
for(int i = 0;i < len1;i++)
{
while(cur_mode > 0 && key[cur_mode] != str[i])
{
cur_mode = mode[cur_mode];
}
if(key[cur_mode] == str[i])
{
cur_mode++;
}
if(cur_mode == len2)
{
cnt++;
}
}
return cnt;
}
void COMPUTE_PREFIX_FUNCTION() //自己和自己匹配的过程
{
mode[1] = 0;
int k = 0; //已匹配长度
for(int i = 2;i <= strlen(key);i++)
{
while(k > 0 && key[k] != key[i - 1])
{
k = mode[k];
}
if(key[k] == key[i - 1])
{
k++;
}
mode[i] = k;
}
}
//7738004 2013-03-11 19:14:48 Accepted 1686 671MS 1268K 1152 B G++ 超级旅行者
算法还是不够快,功力尚浅啊,还需修炼,看看有没有更快的算法。
参考文献:《算法导论》
中查找一个长度为m的数组P[m],如果用朴素字符串匹配方法要用O(mn)的时间,用自动机匹配要O(n)的时间,但一般的自动机要O(ml)的时间(l为字符集的宽度),而KMP只要O(m)的预处理时间。
其实最早接触字符串匹配自动机应该是在数字电路中的序列检测器那时候,序列检测器是用硬件区实现一个个状态的转换,这里和那儿是一个原理。主程序维持一个状态量mode是在读入某个字符后的匹配长度,它随着继续读入字符而不断变化,而变化的函数可以由模式P[m]和字符集去确定,确定这个函数的时间可以为O(ml)(根据《算法导论》,但是我的程序都是O(m^3 * l)的,不知道他是怎么实现的),以杭电1686题为例:
#include<stdio.h> #include<string.h> #include<memory.h> #define MAXM 1000005 #define MAXN 10005 #define MAXW 26 char str[MAXM]; char key[MAXN]; int mode[MAXN][MAXW]; int FINITE_AUTOMATION_MATCHER(); void COMPUTE_TRANSITION_FUNCTION(); int main() { //freopen("Sample Input.txt","r",stdin); int T; scanf("%d",&T); while(T--) { scanf("%s%s",key,str); printf("%d\n",FINITE_AUTOMATION_MATCHER()); } return 0; } int FINITE_AUTOMATION_MATCHER() { int len1 = strlen(str); int len2 = strlen(key); int cnt = 0; int cur_mode = 0; memset(mode,0,sizeof(mode)); COMPUTE_TRANSITION_FUNCTION(); for(int i = 0;i < len1;i++) { cur_mode = mode[cur_mode][str[i] - 'A']; cnt = cur_mode == len2 ? cnt + 1 : cnt; } return cnt; } void COMPUTE_TRANSITION_FUNCTION() //四层循环求转换函数,O(m^3 * l)的复杂度 { int len = strlen(key); for(int i = 0;i <= len;i++) { for(int j = 0;j < 26;j++) //写的有点仓促,很乱。。。。 { int k = i + 1 > len ? len : i + 1; int flag = 0; for(;k > 0;k--) { flag = 1; if(key[k - 1] - 'A' != j) { continue; } for(int s = 0;s < k - 1 && flag;s++) { flag = key[s] == key[i - k + s + 1] ? 1 : 0; } if(flag) { break; } } mode[i][j] = k; } } }
普通的自动机由于转移函数耗时太长TL了。KMP算法类似于自动机它也维持一个暂时的匹配长度mode,在读入下一个字符时如果匹配不成功,那么就要减小mode,求减小的量(前缀函数)就是KMP的核心。前缀函数f[i]是满足是模式P[i]的真后缀和前缀的最大长度,算法中求前缀函数的过程就是模式自己和自己匹配的过程,f[i]就是在读入第i个字符时和模式P的最大匹配长度,因此求前缀函数的过程和匹配过程高度相似,下面还是以hdu1686题为例:#include<stdio.h>
#include<string.h>
#include<memory.h>
#define MAXM 1000005
#define MAXN 10005
char str[MAXM];
char key[MAXN];
int mode[MAXN];
int KMP_MATCHER();
void COMPUTE_PREFIX_FUNCTION();
int main()
{
//freopen("Sample Input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s%s",key,str);
printf("%d\n",KMP_MATCHER());
}
return 0;
}
int KMP_MATCHER()
{
int cnt = 0;
int cur_mode = 0; //相当于自动机中的状态
int len1 = strlen(str);
int len2 = strlen(key);
memset(mode,0,sizeof(mode));
COMPUTE_PREFIX_FUNCTION();
for(int i = 0;i < len1;i++)
{
while(cur_mode > 0 && key[cur_mode] != str[i])
{
cur_mode = mode[cur_mode];
}
if(key[cur_mode] == str[i])
{
cur_mode++;
}
if(cur_mode == len2)
{
cnt++;
}
}
return cnt;
}
void COMPUTE_PREFIX_FUNCTION() //自己和自己匹配的过程
{
mode[1] = 0;
int k = 0; //已匹配长度
for(int i = 2;i <= strlen(key);i++)
{
while(k > 0 && key[k] != key[i - 1])
{
k = mode[k];
}
if(key[k] == key[i - 1])
{
k++;
}
mode[i] = k;
}
}
//7738004 2013-03-11 19:14:48 Accepted 1686 671MS 1268K 1152 B G++ 超级旅行者
算法还是不够快,功力尚浅啊,还需修炼,看看有没有更快的算法。
参考文献:《算法导论》
相关文章推荐
- 字符串匹配算法SMA 总结之四:自动机算法
- 字符串匹配:从后缀自动机到KMP
- 字符串匹配自动机的算法原理
- 利用有限自动机进行字符串匹配
- 利用有限自动机进行字符串匹配
- 字符串匹配自动机
- 字符串匹配(string matching)算法之二:利用有限自动机
- 字符串匹配算法 之 基于DFA(确定性有限自动机)的字符串模式匹配算法
- 字符串匹配自动机
- 多模字符串匹配算法之AC自动机—原理与实现
- 利用有限自动机进行字符串匹配
- 字符串匹配算法 之 基于DFA(确定性有限自动机)的字符串模式匹配算法
- 算法——字符串匹配之有限自动机算法
- HDU3065(病毒侵袭持续中)字符串匹配-简单的AC自动机
- 【总结】字符串匹配: ac自动机
- 字符串匹配算法 之 基于DFA(确定性有限自动机)
- 第32章 :字符串匹配—有限自动机算法,Knuth-Morris-Pratt算法
- 利用有限自动机进行字符串匹配
- 利用有限自动机进行字符串匹配