模板_KMP和AC自動機

KMP

只貼下代碼哈,資料網上很多的啦~

例題 POJ3461

#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
const int maxn=1000010;
char a[maxn],b[maxn];
int next[maxn],la,lb;

void build(){
	next[0]=0;
	for (int i=1,j=0;i<lb;i++){
		while (j!=0 && b[i]!=b[j])
			j = next[j-1];
		if (b[i]==b[j])	j++;
		next[i] = j;
	}
}

void work(){
	int ans=0;
	for (int i=0,j=0;i<la;i++){
		while (j!=0 && a[i]!=b[j])
			j = next[j-1];
		if (a[i]==b[j]) j++;
		if (j == lb){
			++ans;
			j = next[j-1];
		}
	}
	printf("%d\n",ans);
}

int main(){
	int t;
	scanf("%d",&t);
	for (int i=0;i<t;i++){
		scanf("%s",b);
		scanf("%s",a);
		la = strlen(a);
		lb = strlen(b);
		build();
		work();
	}
	return 0;
}

AC自動機

例題 HDU2222


無優化

insert:

先建字典樹

pre:(按照BFS序進行)

失配點:i->fail=j表示1-j是1-i的最大後綴

沿着fa的失配點不斷向上找,直到存在next[ch],則當前結點的失配點指向next[ch]。若不存在,則失配點指向root。

work:

掃主串,若不存在next[ch],就沿着fa的失配點不斷向上找,直到存在next[ch]。每掃到一個結點,沿着失配點一直退到根節點。過程中,若當前結點有標記,則ans+=count。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1000010;
char a[maxn],b[60];
struct node{
	int count;
	node *fail,*next[26];
	node(){
		count = 0;
		fail = NULL;
		for (int i=0;i<26;i++)
			next[i] = NULL;
	}
}*root;
queue<node*> q;

void insert(){
	node *p = root;
	int len = strlen(b),index;
	for (int i=0;i<len;i++){
		index = (int)b[i] - 97;
		if (p->next[index] == NULL)
			p->next[index] = new node();
		p = p->next[index];
	}
	p->count++;
}

void pre(){
	node *p,*temp;
	q.push(root);
	while (!q.empty()){
		p = q.front();
		q.pop();
		for (int i=0;i<26;i++)
			if (p->next[i] != NULL){
				temp = p->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.push(p->next[i]);
			}
	}
}

void work(){
	node *p = root,*temp;
	int ans = 0,len = strlen(a),index;
	for (int i=0;i<len;i++){
		index = int(a[i]) - 97;
		while (p->next[index] == NULL && p != root)
			p = p->fail;
		p = p->next[index];
		if (p == NULL) p = root;
		temp = p;
		while (temp != root && temp->count != -1){
			ans += temp->count;
			temp->count = -1;
			temp = temp->fail;
		}
	}
	printf("%d\n",ans);
}

int main(){
	int t,n;
	scanf("%d",&t);
	for (int i=0;i<t;i++){
		root = new node();
		scanf("%d",&n);
		getchar();
		for (int j=0;j<n;j++){
			gets(b);
			insert();
		}
		pre();
		scanf("%s",a);
		work();
	}
	return 0;
}

加優化

增加失配邊(仍用next數組表示),就不需要一直回溯找失配點,只要找當前結點的失配邊。

p->next[ch]即爲p->fail->next[ch]。(失配邊

p->next[i]->fail 即爲 p->fail->next[i]。(失配點)

仍按照BFS序進行。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1000010;
char a[maxn],b[60];
struct node{
	int count;
	node *fail,*next[26];
	node(){
		count = 0;
		fail = NULL;
		for (int i=0;i<26;i++)
			next[i] = NULL;
	}
}*root;
queue<node*> q;

void insert(){
	node *p = root;
	int len = strlen(b),index;
	for (int i=0;i<len;i++){
		index = (int)b[i] - 97;
		if (p->next[index] == NULL)
			p->next[index] = new node();
		p = p->next[index];
	}
	p->count++;
}

void pre(){
	node *p(root),*temp;
	for (int i=0;i<26;i++)
		if (p->next[i] != NULL){
			p->next[i]->fail = root;
			q.push(p->next[i]);
		}
		else p->next[i] = root;
	while (!q.empty()){
		p = q.front();
		q.pop();
		for (int i=0;i<26;i++)
			if (p->next[i] != NULL){
				p->next[i]->fail = p->fail->next[i];
				q.push(p->next[i]);
			}
			else p->next[i] = p->fail->next[i];
	}
}

void work(){
	node *p = root,*temp;
	int ans = 0,len = strlen(a),index;
	for (int i=0;i<len;i++){
		index = int(a[i]) - 97;
		p = p->next[index];
		if (p == NULL) p = root;
		temp = p;
		while (temp != root && temp->count != -1){
			ans += temp->count;
			temp->count = -1;
			temp = temp->fail;
		}
	}
	printf("%d\n",ans);
}

int main(){
	int t,n;
	scanf("%d",&t);
	for (int i=0;i<t;i++){
		root = new node();
		scanf("%d",&n);
		getchar();
		for (int j=0;j<n;j++){
			gets(b);
			insert();
		}
		pre();
		scanf("%s",a);
		work();
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章