/*AC自动机应用 多模式匹配 多个单词在文章中出现的次数*/
#define N 500
char str[1000],keyword[51];
int head,tail;
// 表示字典树的一个节点
struct node{
node * fail;
node * next[26];
int count;
node(){
fail = NULL;
count = 0;
for(int i=0;i<26;i++)
next[i] = NULL;
}
} * q[N]; // q,用来逐层访问时的队列结构
node * root;
void insert(char * str){
int index,i;
node * p = root; // p代表当前的节点,开始为根节点
int len = strlen(str);
for(i=0;i<len;i++){
index = str[i] - 'a';
if( p->next[index] == NULL)
p->next[index] = new node();
p = p->next[index];
}
p->count++; // 单词的结束节点count不为0,单词组中有多个相同单词,count>1
}
// 设置失败指针
void build_ac( ){
q[tail++] = root; //入队
while(head!=tail){
node *p = q[head++];
node *temp = NULL;
for(int i=0;i<26;i++){ // 处理子节点
if(p->next[i]!=NULL){
if(p==root){
p->next[i]->fail = root;
}else{
temp = p->fail; // 父节点的fail,沿着fail找,直到找到匹配或者到达根
while(temp!=NULL){ // 中间节点
if(temp->next[i]!=NULL){ // 匹配
p->next[i]->fail = temp->next[i];
break;
}
temp = temp ->fail;
}
if(temp == NULL){
p->next[i]->fail = root;
}
}
q[tail++] = p->next[i];
}
}
}
}
int query(char str[]){
int index,len,result;
node * p = root;
result = 0;
len = strlen(str);
for(int i=0;i<len;i++){
index = str[i]-'a';
while(p->next[index]==NULL&&p!=root) // 所有可能匹配的节点
p = p->fail;
p = p->next[index]; // 状态转移
if (p==NULL) // 表示从root转移过来
p = root;
node * temp = p;
// 判断是否是结束单词
while(temp!=root && temp->count!=-1 ){ //&& temp->count!=0
result += temp->count;
temp->count = -1; // 单词已经出现了,不再计数
temp = temp->fail; // 还有可能匹配的单词,其他的前缀为现在的后缀
}
}
return result;
}
int main(){
int ncase,num;
ncase=1;
strcpy(str,"abcdabc");
while(ncase--){
head = tail = 0;
root = new node;
char a[]="abc";
insert(a);
char b[]="bcd";
insert(b);
char c[]="bc";
insert(c);
build_ac( );
cout<<query(str)<<endl;
}
return 0;
}
AC自动机应用 多模式匹配 多个单词在文章中出现的次数-C语言实现
最新推荐文章于 2021-10-12 00:10:50 发布