KMP匹配以及AC自动机
KMP匹配
对于初学字符串的人来说 kmp算法算是一个比较难以理解的算法,但是它在字符串匹配问题的效率中是很高的。
核心思想:
匹配过程中 对于每一次失败的匹配所提供的有用信息加以利用。
abcabcabcd和abcabcd进行匹配
匹配到 abcabc a
abcabc d的时候匹配失败我们可以直接跳转到
匹配 abcabc a
abc a
即跳转到它的最大首尾相同的字符子串
首先我们抛出一个问题:
给你一段任意字符串 求它的所有长度大于2的前缀的最大首尾相同的字符子串长度记作 n e [ l e n ] , l e n ne[len] , len ne[len],len为前缀长度
什么是首尾相同的字符子串:
如 abcsssacabc 它的首位相同的字符子串为abc 长度为3 并且子串不能是前缀本身
想法:
对于长度为2的前缀来说
如果 a [ 1 ] = = [ 2 ] a[1]==[2] a[1]==[2] 那么 n e [ 2 ] = 1 ne[2] = 1 ne[2]=1,否则 n e [ 2 ] = 0 ne[2] = 0 ne[2]=0;
对于长度为3的前缀来说
在匹配长度为2的前缀匹配成功的基础上如果 有 $a[2]==a[3] $那么 n e [ 3 ] = 2 ne[3] = 2 ne[3]=2;
如果 a [ 2 ] ! = a [ 3 ] a[2] != a[3] a[2]!=a[3] 那么就可以令 匹配串 的序号 j = n e [ 2 ] j = ne[2] j=ne[2]
在匹配长度为2的前缀匹配失败的基础上–对3的操作如同对长度2的操作
后面的过程类推 可以写出以下代码
void get_ne()
{
//ne[0] = ne[1] = 0
for(int i=2,j=0;i<=n;i++)
{
while(j&&a[i]!=a[j+1])j = ne[j];
if(a[i]==a[j+1])j++;
ne[i] = j;
}
}
在有了 n e [ ] ne[] ne[]数组的情况下
kmp算法就很简单理解了
void kmp()
{
//i是主串的迭代器 j是匹配串的迭代器
for(int i=1,j=0;i<=m;i++)
{
while(j&&b[i]!=a[j+1])j = ne[j];
if(b[i]==a[j+1])j++;
if(j==m)
{
cout<<i-n<<endl;
j = ne[j];
}
}
}
AC自动机
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e4+10,M = 1e6+10;
int tri[N*55][27],idx;
int ne[N*55];
char ch[M];
int cnt[N*55];
queue<int> q;
void build()
{
int p = 0;
for(int i=0;ch[i];i++)
{
int c = ch[i] - 'a';
if(!tri[p][c])tri[p][c] = ++idx;
p = tri[p][c];
}
cnt[p]++;
}
void init()
{
for(int i=0;i<26;i++)
if(tri[0][i])
q.push(tri[0][i]);
while(q.size())
{
int t = q.front();
q.pop();
for(int i=0;i<26;i++)
{
int p = tri[t][i];
if(!p)tri[t][i] = tri[ne[t]][i];
else
{
ne[p] = tri[ne[t]][i];
q.push(p);
}
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(tri,0,sizeof tri);
memset(cnt,0,sizeof cnt);
memset(ne,0,sizeof ne);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%s",ch);
build();
}
scanf("%s",ch);
init();
int ans = 0;
for(int i=0,j=0;ch[i];i++)
{
int t = ch[i] - 'a';
j = tri[j][t];
int p = j;
while(p&&cnt[p]!=-1)
{
ans += cnt[p];
cnt[p] = -1;
p = ne[p];
}
}
cout<<ans<<endl;
}
return 0;
}