POJ2778 Matrix Fast Power + AC Automata

質問の意味:長さがnの4つのACGT文字が与えられた場合、長さnの4つの文字列のうち、パターン文字列を含まない4つの文字列だけがいくつあるかを尋ね、これらの4つの文字で構成されるm文字列を与えます

アイデア:nの数が2e9レベルであることを考えると、1つのO(n)でも解決できないため、通常のトラバーサルは不可能です。長さnの文字列、これはトライツリーのn個のノードによって(転送されて)取得された文字列であることがわかります。この文字列に以前に指定したパターン文字列が含まれていない場合、これは以前に使用したものと同等です。パターン文字列の終了ノードを通過せずにルートノードからトライを作成する方法はいくつかあります。

離散数学では、グラフ内のある点iから別の点jへの到達可能な関係を見つけるには、グラフの隣接する行列でnを累乗するだけでよいという結論があります。iからjまでのステップ数を取得できます。メソッドの種類。したがって、必要なのは、トライ番号の隣接行列を見つけ、それを使用してすばやく解くだけです。

ps:ここで、ビルド関数では、pの各ノードループが完了した後、現在のpノードのフェイルポインターが指すノードの識別ステータスを確認する必要があります。フェイルポインターが指すノードが特定のパターン文字列の終了ノードである場合、次に、現在のポイントpを到達不能ポイントとして設定する必要があります。そうしないと、このポイントpがパターン文字列の終了ノードに渡されます。(つまり、end [p] | = end [fail [p]];)

ACコード:

#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;
}

おすすめ

転載: blog.csdn.net/weixin_45672411/article/details/108370670