POJ 2778 DNA Sequence AC自动机+矩阵优化 *

本文提供了一种使用前缀树和矩阵快速幂解决 POJ 2778 的方法,该题涉及字符串匹配和大规模数据处理。文章首先介绍了滚动数组的方法并分析了其不足之处,随后提出利用矩阵优化来提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址:http://poj.org/problem?id=2778

数据很大,二维数组开不下,所以第一次写用了滚动数组,但是TLE

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<string>
#include<vector>
using namespace std;
typedef long long LL;
const int letter=4;
const int INF=0x3f3f3f3f;
struct Node{
	Node *pChilds[letter],*pPrev;
	bool bBadNode;
	Node(){
		memset(pChilds,0,sizeof(pChilds));
		pPrev=NULL; bBadNode=false;
	}
}tree[100+5];
char patten[20+5];
LL d[2][100+5];
int nNode;
map<char,int> idx;
//d[i][j]要长度为i到达节点j,也即是tree[j] 字符串个数 
void Insert(Node* root,char* s)
{
	for(int i=0;s[i];i++)
	{
		if(root->pChilds[idx[s[i]]]==NULL)
			root->pChilds[idx[s[i]]]=tree+nNode++;
		root=root->pChilds[idx[s[i]]];
	}
	root->bBadNode=true;
}
void BuildDfa()
{
	for(int i=0;i<letter;i++)
		tree[0].pChilds[i]=tree+1;
	tree[1].pPrev=tree;
	tree[0].pPrev=NULL;
	queue<Node*> Q;
	Q.push(tree+1);
	while(!Q.empty())
	{
		Node* root=Q.front(); Q.pop();
		for(int i=0;i<letter;i++)
		{
			Node* p=root->pChilds[i];
			if(p==NULL) continue;
			Node* pPrev=root->pPrev;
			while(pPrev!=NULL){
				if(pPrev->pChilds[i]!=NULL){
					p->pPrev=pPrev->pChilds[i];
					if(p->pPrev->bBadNode) p->bBadNode=true;
					break;
				}
				else pPrev=pPrev->pPrev;
			}
			Q.push(p);
		}
	}
}
int main()
{
	idx['A']=0;idx['T']=1,idx['G']=2,idx['C']=3;
	int N,M; cin>>M>>N; nNode=2;
	for(int i=0;i<M;i++){
		scanf("%s",patten);
		Insert(tree+1,patten);
	}
	BuildDfa();
	d[0][1]=1;
	for(int i=0;i<N;i++)
	{
		memset(d[i%2^1],0,sizeof(d[0]));
		for(int j=1;j<nNode;j++)
		{
			if(tree[j].bBadNode) continue;
			for(int k=0;k<letter;k++)
			{
				Node* pPrev=tree+j;
				while(pPrev){
					if(pPrev->pChilds[k]!=NULL){
						if(pPrev->pChilds[k]->bBadNode) break;
						int son=pPrev->pChilds[k]-tree;
						d[i%2^1][son]+=d[i%2][j];
						if(d[i%2^1][son]>100000) d[i%2^1][son]-=100000;
						break;
					} 
					pPrev=pPrev->pPrev;
				} 
			}	
		}
	}

	LL ans=0;
	for(int i=1;i<nNode;i++) {
		ans+=d[N%2][i];
		if(ans>100000)  ans-=100000;
	} 
	cout<<ans;
	return 0;
}


下面用矩阵优化

d[i][j]表示i节点到j节点的个数

也即是 d[i][j]=∑ d[i][k]+d[n][k]

这和矩阵的运算法则一样

所以直接矩阵快速幂n次就好了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<string>
#include<vector>
using namespace std;
typedef long long LL;
const int letter=4;
const int INF=0x3f3f3f3f;
struct Matrix{  
    LL a[100+5][100+5];  
    int r; // r*r   
    Matrix(int r=100+5):r(r){memset(a,0,sizeof(a));} //初始化长  
    void MakeI(int x){             //变为x*E的单位矩阵   
        memset(a,0,sizeof(a));  
        for(int i=0;i<r;i++) a[i][i]=x;  
    }  
    Matrix operator * (const Matrix& m) {  //矩阵乘法   
        Matrix result(r);  
        for(int i=0;i<r;i++)  
        for(int j=0;j<r;j++)  
        {  
            for(int k=0;k<r;k++)  
                result.a[i][j]+=a[i][k]*m.a[k][j];  
        }  
        return result;  
    }  
    Matrix operator % (const int p){ //矩阵对每个元素取余   
        Matrix result(*this);  
        for(int i=0;i<r;i++)  
        for(int j=0;j<r;j++)  
            result.a[i][j]%=p;  
        return result;  
    }  
    Matrix operator + (const Matrix& m){  //矩阵互相相加   
        Matrix result(r);  
        for(int i=0;i<r;i++)  
        for(int j=0;j<r;j++)  
            result.a[i][j]=a[i][j]+m.a[i][j];  
        return result;  
    }  
    Matrix operator + (int n){     //加上个常数   
        Matrix result(r);  
        result.MakeI(n);  
        return *this+result;  
    }  
}; 
Matrix PowMod(const Matrix& m,int k,int p)  //矩阵快速幂  
{   //m^k mod p  
    int r=m.r;  
    Matrix result(r);  
    result.MakeI(1); //变成单位矩阵   
    Matrix base=m;  
    while(k){  
        if(k&1) result=result*base%p;  
        base=base*base%p;  
        k>>=1;  
    }   
    return result;  
}
struct Node{
	Node *pChilds[letter],*pPrev;
	bool bBadNode;
	Node(){
		memset(pChilds,0,sizeof(pChilds));
		pPrev=NULL; bBadNode=false;
	}
}tree[100+5];
char patten[20+5];
int nNode;
map<char,int> idx;
//d[i][j] 指节点i到j的步数  i,j都是安全节点,且其中也不经过危险节点 
void Insert(Node* root,char* s)
{
	for(int i=0;s[i];i++)
	{
		if(root->pChilds[idx[s[i]]]==NULL)
			root->pChilds[idx[s[i]]]=tree+nNode++;
		root=root->pChilds[idx[s[i]]];
	}
	root->bBadNode=true;
}
void BuildDfa()
{
	for(int i=0;i<letter;i++)
		tree[0].pChilds[i]=tree+1;
	tree[1].pPrev=tree;
	tree[0].pPrev=NULL;
	queue<Node*> Q;
	Q.push(tree+1);
	while(!Q.empty())
	{
		Node* root=Q.front(); Q.pop();
		for(int i=0;i<letter;i++)
		{
			Node* p=root->pChilds[i];
			if(p==NULL) continue;
			Node* pPrev=root->pPrev;
			while(pPrev!=NULL){
				if(pPrev->pChilds[i]!=NULL){
					p->pPrev=pPrev->pChilds[i];
					if(p->pPrev->bBadNode) p->bBadNode=true;
					break;
				}
				else pPrev=pPrev->pPrev;
			}
			Q.push(p);
		}
	}
}
int main()
{
	idx['A']=0;idx['T']=1,idx['G']=2,idx['C']=3;
	int N,M; cin>>M>>N; nNode=2;
	for(int i=0;i<M;i++){
		scanf("%s",patten);
		Insert(tree+1,patten);
	}
	BuildDfa();
	
	Matrix d(nNode);
	for(int i=1;i<nNode;i++)
	{
		if(tree[i].bBadNode) continue;
		for(int j=0;j<letter;j++)
		{
			Node* pPrev=tree+i;
			while(pPrev){
				if(pPrev->pChilds[j]!=NULL){
					if(pPrev->pChilds[j]->bBadNode) break;
					int son=pPrev->pChilds[j]-tree;
					d.a[i][son]++;
					if(d.a[i][son]>=100000) d.a[i][son]-=100000;
					break;
				} 
				pPrev=pPrev->pPrev;
			} 
		}
	} 
	
	Matrix m=PowMod(d,N,100000);
	LL ans=0;
	for(int i=1;i<nNode;i++)
	if(!tree[i].bBadNode) {
		ans+=m.a[1][i];
		if(ans>=100000) ans-=100000;
	}
	cout<<ans;
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值