问题描述:
给出模式串P和文本串T
有以下问题:1、模式串是否出现在文本串。2、文本串中出现模式串的所有位置。3、模式串在文本串出现的次数。(暂时想不出那么多)
对于上述的每个问题都可以逐个字符比较来找出答案。代码如下
for(int i=0;i<len(T)-len(S);i++)
{
int j=0;
while(j<len(S))
{
if(T[i+j]==P[j])j++;
else break;
}
if(j==len(S))
{
//对应操作
}
}
n=len(T),m=len(S)。显然上述算法的时间复杂度是O(n*m)。
对于k个模式串问题时,时间复杂度就是O(n*m*k)。
对于一个模式串的匹配问题,可以使用KMP算法。
KMP算法的核心在于利用模式串的特点,匹配失效时不是把模式串简单地后移一位,而是根据已匹配的部分进行移位。
T:ababacabacadagh S:abacab
ababadabacadagh
abacab
朴素算法中模式串S失效后,模式串向后一位,如下图
ababadabacadagh
abacab
而在KMP算法中,模式串如下移位
ababadabacadagh
abacab
由上图可知,字符比较的位置不变,依然在T的第6个字符,模式串向后移动两个位置。时间复杂度可以达到O(n)
进行这样的移位,需要先构造模式串的失效指针fail。
构造失效指针代码如下:
int i,j;
fail[0]=-1;
j=-1;i=0;
while(i<n)
{
if(j==-1||ch[i]==ch[j])
{
i++;
j++;
fail[i]=j;
}
else j=fail[j];
}
失效指针的使用:
i=0;j=-1;
while(i<len(T))
{
while(j!=-1&&s[i]!=ch[j])j=fail[j];
i++;
j++;
if(j==len(S))
{
//对应操作
}
}
通过KMP算法,单模式串匹配的时间复杂度就降为O(n+m)。
而在k模式串的情况下,可以对每个模式串使用KMP算法来进行匹配,设l=|所有模式串的长度之和|,则时间复杂度为O(kn+l)。
在模式串较多的时候,该方法就不好用了。
AC自动机就可以解决多模式串匹配问题。
代码如下:
struct ACTree
{
int ch[maxnode][maxc];//ch[i][j]为i结点指向字符j的边,如果不存在则设为非法值
int cot;//记录结点数量
int fail[maxnode];//失效指针
int val[maxnode];//结点储存的信息
int newnode()
{
for(int i=0;i<maxc;i++)
ch[cot][i]=-1;
val[cot]=0;
return cot++;
}
void init(){cot=0;newnode();}
int idx(char c)
{
返回对应字符下标值
}
void insert(char *s)
{
int u=0,n=strlen(s);
int v;
for(int i=0;i<n;i++)
{
v=idx(s[i]);
if(ch[u][v]==-1)
{
ch[u][v]=newnode();
}
u=ch[u][v];
}
val[u]=1;//根据情况设置
}
void build()//构造AC自动机
{
int u=0;
queue<int>q;
fail[0]=0;//根结点的失效指针指向根结点
for(int i=0;i<maxc;i++)
{
if(ch[0][i]==-1)
ch[0][i]=0;
else
{
fail[ch[0][i]]=0;//根结点下一个字符结点的失效指针指向根结点
q.push(ch[0][i]);
}
}
while(!q.empty())
{
u=q.front();q.pop();
for(int i=0;i<maxc;i++)
{
if(ch[u][i]==-1)
ch[u][i]=ch[fail[u]][i];
else
{
fail[ch[u][i]]=ch[fail[u]][i];
q.push(ch[u][i]);
}
}
}
}
void match(char *s)
{
int n=strlen(s);
int u=0,v,temp;
for(int i=0;i<n;i++)
{
v=idx(s[i]);
u=ch[u][v];
temp=u;
while(temp)
{
if(val[temp]>0)
{
//对应操作
}
temp=fail[temp];
}
}
}
};
AC自动机构造时,把结点不存在的边指向。在匹配时就不需要判断。即ch[u][i]=ch[fail[u]][i],不存在的边指向失效指针的对应字符结点。
这样匹配的时间复杂度为O(n),构造AC自动机的时间复杂度为O(l)。
总时间复杂度是O(l+n)。