KMP
概述:
在长度为n的文本S中,找到某个长度为m的关键词P,类似这种问题的解决。
这两个字符串都是很一般的情况时(寻找字串很复杂时),用到KMP更简便。
一些特殊的情况时,用暴力比较简单。
理解:
通过分析P对P进行预处理,在匹配S的时候能够跳过一些字符串,达到快速匹配。
两个指针i,j: 指向S的指针i不回溯(走到尾),指向P的指针回溯。
核心: Next[]数组,用于指向j回溯的位置。(通过预处理得到)
例题: hdu287
KMP算法的模板有两部分,一getFail()预计算Next[]数组,二kmp()函数实现在S中找P(注意每次匹配的初始位置)
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000+5;
char str[maxn],pattern[maxn];
int Next[maxn];
int cnt;
int getfail(char *p,int plen)
{
//预计算Next[],失配情况下得到j回溯的位置
Next[0]=0;Next[1]=0;
for(int i=0;i<plen;i++)
{
int j=Next[i];
while(j&&p[i]!=p[j]) j=Next[j];
Next[i+1]=(p[i]==p[j])?j+1:0;
}
}
int kmp(char *s,char *p)
{
//在S中找P
int last=-1;
int slen=strlen(s),plen=strlen(p);
getfail(p,plen);//预计算Next[]数组
int j=0;
for(int i=0;i<slen;i++)
{
while(j&&s[i]!=p[j]) j=Next[j];
if(s[i]==p[j]) j++;//当前位置字符匹配继续
if(j==plen)//完全匹配
{
//匹配起点i+1-plen,终点i
//以下是针对本题的解决
if(i-last>=plen)//判断新的匹配和上一个匹配是否能分开
{
cnt++;
last=i;
}
}
}
}
int main()
{
while(cin>>str)
{
if(str[0]=='#') break;
cin>>pattern;
cnt=0;
kmp(str,pattern);
cout<<cnt<<endl;
}
return 0;
}
AC自动机
KMP是单模拟匹配算法,处理在一个文本串中查找一个模拟串的问题,AC自动机是多模匹配算法,能在一个文本串中同时查找多个不同的模拟串
针对多模拟匹配问题:给定一个长度为n的文本S,以及k个平均长度为m的模拟串,要求搜索这些模拟串的位置。
模板题:hdu2222
(不理解为什么要这样子,对树的结构还是很陌生)
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000000+100;
const int size=26;
const int maxnode=1000000+100;
int n,ans;
bool vis[maxn];
map<string,int>ms;
int ch[maxn][size+5];
int val[maxnode];
int idx(char c)
{
return c-'a';
}
struct Trie
{
int sz;
Trie()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
memset(vis,0,sizeof(vis));//初始化
}
void insert(char *s)
{
int u=0,n=strlen(s);
for(int i=0; i<n; i++)
{
int c=idx(s[i]);
if(!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]++;
}
};
//自动机
int last[maxn],f[maxn];
void print(int j)
{
if(j&&!vis[j])
{
ans+=val[j];
vis[j]=1;
print(last[j]);
}
}
int getfail()
{
queue<int>q;
f[0]=0;
for(int c=0; c<size; c++)
{
int u=ch[0][c];
if(u)
{
f[u]=0;
q.push(u);
last[u]=0;
}
}
while(!q.empty())
{
int r=q.front();
q.pop();
for(int c=0; c<size; c++)
{
int u=ch[r][c];
if(!u)
{
ch[r][c]=ch[f[r]][c];
continue;
}
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void find_T(char *T)
{
int n=strlen(T);
int j=0;
for(int i=0;i<n;i++)
{
int c=idx(T[i]);
j=ch[j][c];
if(val[j]) print(j);
else if(last[j]) print(last[j]);
}
}
char tmp[105];
char text[1000000+1000];
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
Trie trie;
ans=0;
for(int i=0;i<n;i++)
{
cin>>tmp;
trie.insert(tmp);
}
getfail();
cin>>text;
find_T(text);
cout<<ans<<endl;
}
return 0;
}
//并记不住,,
//最后一次码代码,,