Ac自动机 & 矩阵

Posted sduwh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ac自动机 & 矩阵相关的知识,希望对你有一定的参考价值。

参考博客

其实不算很难的一个算法

先建一个 Tire树 ,然后bfs构造 fail 指针

(Fail) 的含义

若一个节点 (i)(fail[i] = j) ,则表示从 (root)(j) 的字符串是 (root)(i) 的字符串的一个后缀

AC自动机洛谷模板

#include<bits/stdc++.h>
using namespace std;

const int N = 6e6 + 10;
queue<int>q;
struct {
	int c[N][26], fail[N], val[N], cnt;
	void insert(char* s) {
		int len = strlen(s); int now = 0;
		for (int i = 0; i < len; i++) {
			int v = s[i] - ‘a‘;
			if (!c[now][v])c[now][v] = ++cnt;
			now = c[now][v];
		}
		val[now]++;
	}
	void getFail() {
		for (int i = 0; i < 26; i++) {
			if (c[0][i])fail[c[0][i]] = 0, q.push(c[0][i]);
		}
		while (!q.empty()) {
			int u = q.front(); q.pop();
			for (int i = 0; i < 26; i++) {
				if (c[u][i]) {
					fail[c[u][i]] = c[fail[u]][i];
					q.push(c[u][i]);
				}
				else c[u][i] = c[fail[u]][i];
			}
		}
	}
	int query(char* s) {
		int len = strlen(s); int now = 0, ans = 0;
		for (int i = 0; i < len; i++) {
			now = c[now][s[i] - ‘a‘];
			for (int t = now; t && val[t] != -1; t = fail[t]) {
				ans += val[t];
				val[t] = -1;
			}
		}
		return ans;
	}
}Ac;

int n;
char p[N];

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%s", p);
		Ac.insert(p);
	}
	Ac.getFail();
	scanf("%s", p);
	printf("%d
", Ac.query(p));
}

Ac自动机 + 矩阵快速幂

Poj2778

其实Ac自动机的Tire树就是一个状态转移图,构造出状态转移矩阵, (M_{ij}) 表示从Tire树上的第 (i) 个节点转移到 (j) 节点的方案数, (M^n) 就是长度为 (n) 的串的状态转移矩阵, (M_{0i}) 表示从根节点转移到 (i) 经过 (n) 次的方案数,(ans= sum_iM_{0i})

在处理Tire树的时候要稍微注意一些小的细节。

主要就是标记的传递

if(val[fail[u]]) val[u] = 1

以输入:

4 3
AT
AC
AG
AA

为例

技术图片

#include<cstdio>
#include<map>
#include<cstring>
#include<queue>
#include<string>
#define int long long
using namespace std;

const int N = 5e5 + 10;
queue<int>q;
const int mod = 1e5;
map<char, int>id;
struct Mat {
	int m[100][100], n;
	Mat(int _n, int v) {
		n = _n;
		memset(m, 0, sizeof m);
		for (int i = 0; i < n; i++)m[i][i] = v;
	}
	Mat operator *(const Mat& b)const {
		Mat res = Mat(b.n, 0);
		int n = b.n;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				for (int k = 0; k < n; k++) {
					res.m[i][j] = (res.m[i][j] + m[i][k] * b.m[k][j]) % mod;
				}
			}
		}
		return res;
	}
};
struct {
	int c[N][4], fail[N], val[N], cnt;
	void insert(char* s) {
		int len = strlen(s); int now = 0;
		for (int i = 0; i < len; i++) {
			int v = id[s[i]];
			if (!c[now][v])c[now][v] = ++cnt;
			now = c[now][v];
		}
		val[now]++;//这里写++好像过不去
		//val[now] = 1;
	}
	void clear() {
		memset(c, 0, sizeof c);
		memset(val, 0, sizeof val);
		cnt = 0;
		memset(fail, 0, sizeof fail);
	}
	void getFail() {
		for (int i = 0; i < 4; i++) {
			if (c[0][i])fail[c[0][i]] = 0, q.push(c[0][i]);
		}


		while (!q.empty()) {
			int u = q.front(); q.pop();

			//***
			if (val[fail[u]] == 1) {
				val[u] = 1;
			}

			for (int i = 0; i < 4; i++) {
				if (c[u][i]) {
					fail[c[u][i]] = c[fail[u]][i];
					q.push(c[u][i]);
				}
				else c[u][i] = c[fail[u]][i];
			}
		}
	}
	int query(char* s) {
		int len = strlen(s); int now = 0, ans = 0;
		for (int i = 0; i < len; i++) {
			now = c[now][id[s[i]]];
			for (int t = now; t && val[t] != -1; t = fail[t]) {
				ans += val[t];
				val[t] = -1;
			}
		}
		return ans;
	}
	Mat getMat() {
        //这里是cnt + 1
		Mat res = Mat(cnt+1, 0);
		for (int i = 0; i <= cnt; i++) {
			for (int j = 0; j < 4; j++) {
				if (!val[c[i][j]]) {
					res.m[i][c[i][j]]++;
				}
			}
		}
		return res;
	}

}Ac;

Mat qpow(Mat a, int p) {
	Mat res = Mat(a.n, 1);
	while (p) {
		if (p & 1) res = a * res;
		a = a * a;
		p >>= 1;
	}
	return res;
}

int n;
char p[N];


signed main() {
	char s[] = "ACGT";
	for (int i = 0; i < 4; i++)id[s[i]] = i;

	int n, m, x;
	while (~scanf("%lld%lld", &m, &n)) {
		Ac.clear();
		for (int i = 0; i < m; i++) {
			scanf("%s", p);
			Ac.insert(p);
		}
		Ac.getFail();
		Mat mat = Ac.getMat();
		mat = qpow(mat, n);

		int ans = 0;
		for (int i = 0; i < mat.n; i++) {
			ans = (ans + mat.m[0][i]) % mod;
		}
		printf("%lld
", ans);
	}
}

牛客Day1 G. K==S

其实就是一个模板题

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 7e5 + 10;
queue<int>q;
const int mod = 1e9 + 7;

struct Mat {
	int m[500][500], n;
	Mat(int _n,int v) {
		n = _n;
		memset(m, 0, sizeof m);
		for (int i = 0; i < n; i++)m[i][i] = v;
	}
	Mat operator *(const Mat& b)const {
		Mat res = Mat(b.n,0);
		int n = b.n;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				for (int k = 0; k < n; k++) {
					res.m[i][j] = (res.m[i][j] + m[i][k] * b.m[k][j]) % mod;
				}
			}
		}
		return res;
	}
};
struct {
	int c[N][26], fail[N], val[N], cnt;
	void insert(char* s) {
		int len = strlen(s); int now = 0;
		for (int i = 0; i < len; i++) {
			int v = s[i] - ‘a‘;
			if (!c[now][v])c[now][v] = ++cnt;
			now = c[now][v];
		}
		//val[now]++;
        val[now] = 1;
	}
	void getFail() {
		for (int i = 0; i < 26; i++) {
			if (c[0][i])fail[c[0][i]] = 0, q.push(c[0][i]);
			//***
			else c[0][i] = 0;
		}
		
		
		while (!q.empty()) {
			int u = q.front(); q.pop();

			//***
			if (val[fail[u]] == 1) {
				val[u] = 1;
			}

			for (int i = 0; i < 26; i++) {
				if (c[u][i]) {
					fail[c[u][i]] = c[fail[u]][i];
					q.push(c[u][i]);
				}
				else c[u][i] = c[fail[u]][i];
			}
		}
	}
	int query(char* s) {
		int len = strlen(s); int now = 0, ans = 0;
		for (int i = 0; i < len; i++) {
			now = c[now][s[i] - ‘a‘];
			for (int t = now; t && val[t] != -1; t = fail[t]) {
				ans += val[t];
				val[t] = -1;
			}
		}
		return ans;
	}
	Mat getMat() {
        //这里 cnt 也能过,但是上面的POJ会wa,这里数据的问题,应该是cnt+1
		Mat res = Mat(cnt + 1, 0);
		for (int i = 0; i <= cnt; i++) {
			for (int j = 0; j < 26; j++) {
				if (!val[c[i][j]]) {
					res.m[i][c[i][j]]++;
				}
			}
		}
		return res;
	}

}Ac;

Mat qpow(Mat a, int p) {
	Mat res = Mat(a.n, 1);
	while (p) {
		if (p & 1) res = a * res;
		a = a * a;
		p >>= 1;
	}
	return res;
}

int n;
char p[N];

signed main() {
	int n, m, x;
	scanf("%lld%lld", &n, &m);
	for (int i = 0; i < m; i++) {
		scanf("%lld%s", &x, p);
		Ac.insert(p);
	}
	Ac.getFail();
	Mat mat = Ac.getMat();
	mat = qpow(mat, n);

	int ans = 0;
	for (int i = 0; i < mat.n; i++) {
		ans = (ans + mat.m[0][i]) % mod;
	}
	printf("%lld
", ans);
}

以上是关于Ac自动机 & 矩阵的主要内容,如果未能解决你的问题,请参考以下文章

POJ2778&HDU2243&POJ1625(AC自动机+矩阵/DP)

HDU2243 考研路茫茫——单词情结(AC自动机+矩阵快速幂)

POJ 1204 Word Puzzles | AC 自动鸡

POJ 2778 DNA Sequence(AC自动机+矩阵)

HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

POJ 2778 DNA Sequence (AC自动机+DP+矩阵)