POJ2778 Matrix Fast Power + AC Automata

Question meaning: Given four ACGT characters, given a length of n, ask you how many of the four strings of length n only contain these four strings but not the pattern string, give m pattern strings composed of these four characters

Idea: Given the number of n is 2e9 level, so it cannot be solved even for one O(n), and ordinary traversal is definitely not feasible. A string of length n, we know that it is a string obtained by (transferring) n nodes on the trie tree, and if this string does not contain the pattern string we gave before, it is equivalent to what we used to There are several ways to construct a trie from the root node without passing through the end node of the pattern string.

There is a conclusion in discrete mathematics that to find the reachable relationship from one point i to another point j in a graph, only need to power n in the adjacent matrix of the graph, you can get the number of steps from i to j Kind of method. Therefore, it is only necessary to find the adjacent matrix of the trie number, and then use the matrix to solve it quickly.

ps: Note here that in the build function, after each node loop of p is completed, we need to look at the identification status of the node pointed to by the fail pointer of the current p node. If the node pointed to by the fail pointer is the end node of a certain pattern string, Then we have to set the current point p as an unreachable point accordingly, otherwise it will pass this point p to the end node of a pattern string. (Ie end[p] |= end[fail[p]];)

AC code:

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int MOD = 1e5;
const int MAX_TOT = 107;
const int MAXN = 17;

int m,n;
std::map<char,int>mp;//用一个map实现ACGT 到 0123的映射 这样next只需要开4个节点即可
//矩阵快速幂
struct Matrix
{
    
    
	int mat[MAX_TOT][MAX_TOT],n;
	Matrix(){
    
    }
	Matrix(int num){
    
    
		n = num;
		for(int i = 0;i < n;i ++){
    
    
			for(int j = 0;j < n;j ++) mat[i][j] = 0;
		}
	}
};

Matrix mul(Matrix a,Matrix b)
{
    
    
	Matrix tmp = Matrix(a.n);
	for(int i = 0;i < a.n;i ++){
    
    
		for(int j = 0;j < a.n;j ++){
    
    
			for(int k = 0;k < a.n;k ++){
    
    
				int sum = (ll)a.mat[i][k]*b.mat[k][j]%MOD;
				tmp.mat[i][j] = (tmp.mat[i][j] + sum)%MOD;
			}
		}
	}
	return tmp;
}

Matrix quickpow(Matrix a,ll n)
{
    
    
	Matrix E = Matrix(a.n);
	for(int i = 0;i < a.n;i ++) E.mat[i][i] = 1;
	while(n){
    
    
		if(n&1) E = mul(E,a);
		a = mul(a,a);
		n >>= 1;
	}
	return E;
}
//
struct Aca
{
    
    
	int Next[MAX_TOT][4],fail[MAX_TOT],end[MAX_TOT],size;

	queue<int>q;

	void init()
	{
    
    
		mp['A'] = 0,mp['C'] = 1,mp['T'] = 2,mp['G'] = 3;
		while(!q.empty()) q.pop();
		for(int i = 0;i < MAX_TOT;i ++){
    
    
			end[i] = fail[i] = 0;
			for(int ii = 0;ii < 4;ii ++) Next[i][ii] = 0;
		}
		size = 1;
	}

	void insert(char *s)
	{
    
    
		int len = strlen(s);
		int p = 1;
		for(int i = 0;i < len;i ++){
    
    
			int c = mp[s[i]];
			//printf("%d\n",c);
			if(!Next[p][c]) Next[p][c] = ++size;
			p = Next[p][c];
		}
		end[p] = 1;
	}

	void build()
	{
    
    
		for(int i = 0;i < 4;i ++) Next[0][i] = 1;
		q.push(1);
		fail[1] = 0;
		while(!q.empty()){
    
    
			int p = q.front();
			q.pop();
			for(int i = 0;i < 4;i ++){
    
    
				int now = Next[p][i];
				int fafail = fail[p];
				if(!now){
    
    
					Next[p][i] = Next[fafail][i];
					continue;
				}
				fail[now] = Next[fafail][i];
				q.push(now);
			}
			// 下一层不是结尾节点的点 如果他的fail指向了一个结尾节点 那么代表如果走到了这个节点失配
			//的话 会走到fail处 因为fail是尾结点 所以当前失配位置也应当看做尾结点
			end[p] |= end[fail[p]];//这有一个 end关系的传递要看到 wa吐了
		}
	}

	Matrix getmartix()//获得trie树的临接矩阵
	{
    
    
		Matrix tmp = Matrix(size);
		for(int i = 1;i <= size;i ++){
    
    
			for(int j = 0;j < 4;j ++){
    
    
				if(end[Next[i][j]]) continue;//这个点不可到达 因为他是尾结点
				tmp.mat[i-1][Next[i][j]-1]++;//邻接矩阵可到达
			}
		}
		return tmp;
	}

}aca;
//输出矩阵检查用的
void opMatrix(Matrix a)
{
    
    
	for(int i = 0;i < a.n;i ++){
    
    
		for(int j = 0;j < a.n;j ++){
    
    
			printf(j == a.n-1?"%d\n":"%d ",a.mat[i][j]);
		}
	}
}

void debug(){
    
    
	for(int i = 1;i <= aca.size;i ++){
    
    
		printf("---fail = %d\n",aca.fail[i]);
	}
}

int main()
{
    
    
	char s[MAXN];
	aca.init();
	scanf("%d%d",&m,&n);
	for(int i = 1;i <= m;i ++){
    
    
		scanf("%s",s);
		aca.insert(s);
	}

	aca.build();
	//debug();
	Matrix res = aca.getmartix();

	res = quickpow(res,n);
	//printf("-------------\n");
	//opMatrix(res);
	//printf("-------------\n");
	int ans = 0;
	for(int i = 0;i < res.n;i ++){
    
    
		ans = (ans + res.mat[0][i])%MOD;
	}
	printf("%d\n",ans);
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_45672411/article/details/108370670