传送门:HDU222
给你很多个单词,然后给你一篇文章,问给出的单词在文章中出现的次数。
AC自动机入门题。需要注意的就是可能有重复单词。这里提供两个模板
(飘过的小牛的模板,时间稍快,不过内存消耗大)
//HDU 2222 G++ 327ms 58960k
//构造失败指针:设当前节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
//匹配(1)当前字符匹配,只需沿该路径走向下一个节点继续匹配即可;(2)当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配.重复这2个过程中的一个,直到模式串走完。
#include
using namespace std;
const int maxn=500005;
char s[1000010],key[51];
int head,tail;
struct node{
node *fail;
node *next[26]; //字母的字典序编号
int cnt;
node(){//init
fail = NULL;
cnt = 0;
for(int i = 0; i < 26; ++i)
next[i] = NULL;
}
}*q[maxn];
node *root;
void inser(char *s){//建立trie树
int tmp, len;
node *p = root;
len = strlen(s);
for(int i = 0; i < len; ++i){
tmp = s[i] - 'a';
if(p->next[tmp] == NULL)
p->next[tmp] = new node();
p = p->next[tmp];
}
p->cnt++;
}
void build(){ //初始化fail指针,BFS
q[tail++]=root;
while(head!=tail){
node *p=q[head++]; //弹出队头
node *tmp=NULL;
for(int i=0; i<26; i++){
if(p->next[i]!=NULL){
if(p==root)//第一个元素fail必指向根
p->next[i]->fail=root;
else{
tmp=p->fail;//失败指针
while(tmp!=NULL){//2种情况结束:匹配为空or找到匹配
if(tmp->next[i]!=NULL){//找到匹配
p->next[i]->fail=tmp->next[i];
break;
}
tmp=tmp->fail;
}
if(tmp==NULL)
p->next[i]->fail=root;
}
q[tail++]=p->next[i];//入队
}
}
}
}
int query(){//扫描
int index,len,res;
node *p=root; //Tire入口
res=0;
len=strlen(s);
for(int i=0; inext[index]==NULL && p!=root)//跳转失败指针
p=p->fail;
p=p->next[index];
if(p==NULL)p=root;
node *tmp=p;//p不动,tmp计算后缀串
while(tmp!=root && tmp->cnt!=-1){
res+=tmp->cnt;
tmp->cnt=-1;
tmp=tmp->fail;
}
}
return res;
}
int main(){
int t,n;
scanf("%d",&t);
while(t--){
head=tail=0;
root=new node();
scanf("%d",&n);
getchar();
for(int i=0; i
(bin神的模板,内存消耗小)
//G++ 764ms 27968k
//======================
// HDU 2222
// 求目标串中出现了几个模式串
//====================
#include
using namespace std;
const int maxn=500010;
struct Trie{
int next[maxn][26],fail[maxn],end[maxn];//字母的字典序编号 失败指针 以此结点为末尾的单词个数
int root,L;//根结点 总的结点数
int newnode(){
for(int i = 0;i < 26;i++)
next[L][i] = -1;
end[L++] = 0;
return L-1;
}
void init(){
L = 0;
root = newnode();
}
void insert(char buf[]){//建立trie树
int len = strlen(buf);
int now = root;
for(int i = 0;i < len;i++){
if(next[now][buf[i]-'a'] == -1)
next[now][buf[i]-'a'] = newnode();
now = next[now][buf[i]-'a'];
}
end[now]++;
}
void build(){//初始化fail指针,BFS
queueQ;
fail[root] = root;
for(int i = 0;i < 26;i++)//处理初始的元素
if(next[root][i] == -1)
next[root][i] = root;
else{
fail[next[root][i]] = root;//第一个元素fail必指向根
Q.push(next[root][i]);
}
while( !Q.empty() ){
int now = Q.front();
Q.pop();
for(int i = 0;i < 26;i++)
if(next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
int query(char buf[]){//扫描文本串
int len = strlen(buf);
int now = root;
int res = 0;
for(int i = 0;i < len;i++){
if(buf[i]>='a' && buf[i]<='z')now = next[now][buf[i]-'a'];//结合具体题目修改
else if(buf[i]>='A' && buf[i]<='Z')now = next[now][buf[i]-'A'];
else continue;
int temp = now;
while( temp != root ){
res += end[temp];
end[temp] = 0;
temp = fail[temp];
}
}
return res;
}
void debug(){//调试
for(int i = 0;i < L;i++){
printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
for(int j = 0;j < 26;j++)
printf("%2d",next[i][j]);
printf("]\n");
}
}
};
char buf[1000010];
Trie ac;
int main(){
int T;
int n;
scanf("%d",&T);
while( T-- ){
scanf("%d",&n);
ac.init();////////
for(int i = 0;i < n;i++){
scanf("%s",buf);
ac.insert(buf);////////
}
ac.build();
scanf("%s",buf);//gets scanf 得看情况
printf("%d\n",ac.query(buf));////////
}
return 0;
}