AC自动机就是一种在 Trie树上的kmp,用于多模式串的匹配及对多模式串限制的dp。初始时将所有模式串放进Trie树中,然后在Trie树上构建next数组和fail数组。通过next可以进行转移,通过fail可以找到所有具有相同后缀的模式串。有了next数组,Trie树可以看成一个图。在这个图上可以进行各种dp,一般题目上规定了有很多字符串的限制的时候,应该就要想到AC自动机。
复习AC自动机。
AC自动机是将多个模式串构建成一颗Trie,然后再Trie上构建fail指针。fail指针指向下一个后缀相同的串,复杂度是 O(n) 。
#include<bits/stdc++.h>
using namespace std;
struct Trie
{
int L,root,nxt[500007][26],end[500007],fail[500007];
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[])
{
int len=strlen(buf);
int now=root;
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now=nxt[now][buf[i]-'a'];
}
++end[now];
}
void build()
{
queue<int> q;
fail[root]=root;
for(int i=0;i<26;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(char buf[])
{
int len=strlen(buf);
int tmp,now=root,res=0;
for(int i=0;i<len;++i)
{
now=nxt[now][buf[i]-'a'];
tmp=now;
while(tmp!=root)
{
res+=end[tmp];
end[tmp]=0;
tmp=fail[tmp];
}
}
return res;
}
};
Trie t;
char s[1000007];
char pt[57];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
t.init();
for(int i=0;i<n;++i)
{
scanf("%s",pt);
t.insert(pt);
}
t.build();
scanf("%s",s);
printf("%d\n",t.query(s));
}
return 0;
}
set
维护不同的编号
#include<bits/stdc++.h>
using namespace std;
struct Trie
{
int nxt[100007][128],L,root,fail[100007],end[100007];
int newnode()
{
for(int i=0;i<128;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[],int id)
{
int len=strlen(buf);
int now=root;
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
end[now]=id;
}
void build()
{
queue<int> q;
for(int i=0;i<128;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<128;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
set<int> query(char buf[])
{
int len=strlen(buf);
int now=root,tmp;
set<int> res;
for(int i=0;i<len;++i)
{
now=nxt[now][buf[i]];
tmp=now;
while(tmp!=root)
{
if(end[tmp])
res.insert(end[tmp]);
tmp=fail[tmp];
}
}
return res;
}
};
Trie t;
char pt[207];
char s[10007];
int main()
{
int n;
while(~scanf("%d",&n))
{
t.init();
for(int i=1;i<=n;++i)
{
scanf("%s",pt);
t.insert(pt,i);
}
t.build();
int m,ans=0;
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%s",s);
auto res=t.query(s);
if(res.size())
{
++ans;
printf("web %d:",i);
for(auto k : res) printf(" %d",k);
puts("");
}
}
printf("total: %d\n",ans);
}
return 0;
}
计数。。
#include<bits/stdc++.h>
using namespace std;
int cnt[1007];
struct Trie
{
int L,root,nxt[50007][128],fail[50007],end[50007];
int newnode()
{
for(int i=0;i<128;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(string & buf , int id)
{
int now=root,len=buf.length();
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
end[now]=id;
}
void build()
{
queue<int> q;
for(int i=0;i<128;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<128;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
void query(string & buf)
{
int len=buf.length(),now=root,tmp;
for(int i=0;i<len;++i)
{
now=nxt[now][buf[i]];
tmp=now;
while(tmp!=root)
{
++cnt[end[tmp]];
tmp=fail[tmp];
}
}
}
};
string pt[1007],s;
Trie t;
int main ()
{
ios::sync_with_stdio(false);
int n;
while(cin >> n)
{
t.init();
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;++i)
{
cin >> pt[i];
t.insert(pt[i],i);
}
t.build();
cin >> s;
t.query(s);
for(int i=1;i<=n;++i)
if(cnt[i])
cout << pt[i] << ": " << cnt[i] << '\n';
}
return 0;
}
先base64解码后再搞。。有个RE点就是解码后的字符是uchar的。
#include<bits/stdc++.h>
using namespace std;
string b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int f[128];
int buf[100000];
int str[100000],strl;
void decode(string &s)
{
int len=0;
for(int i=0;i<s.length();++i)
{
if(s[i]=='=')
{
len-=(s.length()-i+1)*2;
break;
}
for(int j=0;j<6;++j)
buf[len+j]=(f[s[i]]>>(6-j-1))&1;
len+=6;
}
strl=0;
for(int i=0;i<len;i+=8)
{
int k=0;
for(int j=0;j<8;++j)
k<<=1,k|=buf[i+j];
str[strl++]=k;
}
}
struct Trie
{
int L,root,nxt[40000][256],fail[40000],end[40000],vis[40000];
int newnode()
{
for(int i=0;i<256;++i)
nxt[L][i]=-1;
end[L]=vis[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(int buf[],int len)
{
int now=root;
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
++end[now];
}
void build()
{
queue<int> q;
for(int i=0;i<256;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<256;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(int buf[],int len ,int id)
{
int now=root,tmp,res=0