PKU 2778 HDU 2243 AC自动机 + 矩阵乘法

9 篇文章 0 订阅
6 篇文章 0 订阅

矩阵乘法:http://blog.csdn.net/vsooda/article/details/8510131

构造trie图,继而构造出初始矩阵,mat[i][j]表示i走到j有几种走法 ,这个矩阵自乘n次之后就表示i走到j走n步有几种走法

问你长度为N的串中不包含了模式串的串有几个

n属于1 ~ 2000000000看到这个数据范围我们就应该敏感的想到这是矩阵~
最多100个结点,先建好所有结点(不包括模式串结尾的和fail指向结尾的结点,所以其实最多只有90个有效结点)之间的转化关系,然后二分矩阵乘法,复杂度O(100^3*log(2000000000))

#include <cstdio>
#include <cstdlib>
#include <string>
#include <climits>
#include <iostream>   
#include <vector>
#include <set>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <sstream>
#include <map>
#include <cstring>
#include <queue>
using namespace std;
const int mod = 100000;
const int M = 100;
const int CD = 4;
int fail[M];
int Q[M];
int ch[M][CD];
int ID[128];
int val[M];
int sz;
void Init(){
	fail[0]=0;
	memset(ch[0],0,sizeof(ch[0]));
	sz=1;
	ID['A']=0;ID['T']=1;ID['G']=2;ID['C']=3;
}
void Insert(char *s){
	int p=0;
	for(;*s;s++){
		int c=ID[*s];
		if(!ch[p][c]){
			memset(ch[sz],0,sizeof(ch[sz]));
			val[sz]=0;
			ch[p][c]=sz++;
		}
		p=ch[p][c];
	}
	val[p]=1;
}
void Construct(){
	int  *s=Q,*e=Q;
	for(int i=0;i<CD;i++){
		if(ch[0][i]){
			fail[ch[0][i]] = 0;
			*e++ = ch[0][i];
		}
	}
	while(s!=e){
		int u = *s++;
		for(int i=0;i<CD;i++){
			int &v = ch[u][i];
			if(v){
				*e++ = v;
				fail[v]=ch[fail[u]][i];
				val[v]|=val[fail[v]];
			} else  {
				v=ch[fail[u]][i];
			}
		}
	}
}
long long dp[100][100];
const int MAX = 100;
int n;
struct  Mat {
	int mat[MAX][MAX];
	Mat(){
		memset(mat,0,sizeof(mat));
	}
	void init(){
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				mat[i][j]= i==j;
	}
	void print(){
		printf("****************\n");
		for(int i=0;i<n;i++) 
			for(int j=0;j<n;j++)
				printf(j==n-1?"%d\n":"%d ",mat[i][j]);
		printf("fuckfuckfuckfuckfuck\n");
	}
	friend Mat operator *(Mat a,Mat b);
	friend Mat operator +(Mat a,Mat b);
	friend Mat operator ^(Mat a,int k);
}E;
Mat operator +(Mat a,Mat b)
{
	Mat c;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			c.mat[i][j]=a.mat[i][j]+b.mat[i][j];
			if(c.mat[i][j]>=mod) c.mat[i][j]-=mod;
		}

		return c;
}
Mat operator *(Mat a,Mat b)
{
	Mat ans;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			long long tmp=0;
			for(int k=0;k<n;k++)
			{
				tmp+=(long long)a.mat[i][k]*b.mat[k][j];
			}
			ans.mat[i][j]=tmp%mod;
		}

		return ans;
}
Mat operator ^(Mat a,int k)
{
	Mat ans=E;
	while(k){
		if(k&1) ans=ans*a;
		a=a*a,k>>=1;
	}
	return ans;
}
int main() {
	char s[15];
	int k,m;
	while(scanf("%d%d",&m,&k)!=EOF)	{
		Init();
		for(int i=0;i<m;i++)  {
			scanf("%s",s);
			Insert(s);
		}
		Construct();
		n=sz;
		Mat ans;
		for(int i=0;i<sz;i++)
		{
			if(val[i])
				continue;
			for(int j=0;j<4;j++)
			{
				if(val[ch[i][j]])
					continue;
				ans.mat[i][ch[i][j]]++;
			}
		}
		E.init();
		ans=ans^k;
		int ret=0;
		for(int i=0;i<n;i++)
		{
			ret+=ans.mat[0][i];
			if(ret>=mod) ret-=mod;
		}
		printf("%d\n",ret);
	}
	return 0;
}


 

 

HDU 2243

问你长度为1~N的串中包含了模式串的串总共有几个

上题的加强版,先要把总数26^1 + 26^2 + … + 26^N算出来,然后减去所有不包含的…反正比上题恶心一点点
答案要模2^64,直接用unsinged __int64 就OK了

 

 

#include<cstdio>
#include<cstring>
typedef unsigned __int64 ULL;
const int MAX = 65;
int n,k,m,tn;
struct  Mat {
	ULL mat[MAX][MAX];
	friend Mat operator *(Mat a,Mat b);
	friend Mat operator +(Mat a,Mat b);
	friend Mat operator ^(Mat a,int k);
}E,A;
ULL a[MAX][MAX];
Mat operator +(Mat a,Mat b)
{
	Mat c;
	memset(c.mat,0,sizeof(c.mat));
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			c.mat[i][j]=(a.mat[i][j]+b.mat[i][j]);
	return c;
}
Mat operator *(Mat a,Mat b)
{
	Mat ans;
	memset(ans.mat,0,sizeof(ans.mat));
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			for(int k=0;k<n;k++)
			{
				ULL tmp=a.mat[i][k]*b.mat[k][j];
				ans.mat[i][j]=ans.mat[i][j]+tmp;
			}
			return ans;
}
Mat operator ^(Mat a,int k)
{
	for(int i=0;i<n;i++) for(int j=0;j<n;j++) E.mat[i][j]= (i==j);
	Mat ans=E;
	while(k){
		if(k&1) ans=ans*a;
		a=a*a,k>>=1;
	}
	return ans;
}
const int M = 100;
const int CD = 26;
int fail[M];
int Q[M];
int ch[M][CD];
int ID[128];
int sz;
int flag[M];
void Init() 
{
	fail[0]=0;
	memset(ch[0],0,sizeof(ch[0]));
	sz=1;
	for(int i=0;i<26;i++) ID[i+'a']=i;
}
void Insert(char *s)
{
	int p=0;
	for(;*s;s++)
	{
		int c=ID[*s];
		if(!ch[p][c])
		{
			memset(ch[sz],0,sizeof(ch[sz]));
			flag[sz]=0;
			ch[p][c]=sz++;
		}
		p=ch[p][c];
	}
	flag[p]=1;
}
void Construct()
{
	int *s=Q,*e=Q,v;
	for(int i=0;i<CD;i++)
	{
		if(ch[0][i])
		{
			fail[ch[0][i]]=0;
			*e++ = ch[0][i];
		}
	}
	while(s!=e)
	{
		int u = *s++;
		for(int i=0;i<CD;i++)
		{
			if(v=ch[u][i])
			{
				*e++=v;
				fail[v]=ch[fail[u]][i];
				flag[v]|=flag[fail[v]];
			}
			else 
			{
				ch[u][i]=ch[fail[u]][i];
			}
		}
	}
}
void init()
{
	memset(A.mat,0,sizeof(A.mat));
	for(int i=0;i<tn;i++)
	{
		for(int j=0;j<tn;j++)
		{
			A.mat[i][j]=a[i][j];
			A.mat[i][j+tn]=a[i][j];
		}
	}
	for(int i=tn;i<n;i++)
	{
		for(int j=tn;j<n;j++)
		{
			if(i==j)A.mat[i][j]=1;
		}
	}
}
int main()
{    
	int N;
	int L;
	char s[10];
	while(scanf("%d%d",&N,&L)!=EOF)
	{
		memset(a,0,sizeof(a));
		tn=1;  a[0][0]=26;   n=2*tn; init();
		Mat ans=A^L;
		ULL sum=ans.mat[0][1];
		Init();
		for(int i=0;i<N;i++)
		{
			scanf("%s",s);
			Insert(s);
		}
		Construct();
		Mat dp;
		memset(dp.mat,0,sizeof(dp.mat));
		for(int i=0;i<sz;i++)if(!flag[i])
		{
			for(int j=0;j<CD;j++) if(!flag[ch[i][j]])
			{
				dp.mat[i][ch[i][j]]++;
			}
		}
		memset(a,0,sizeof(a));
		for(int i=0;i<sz;i++)
		{
			for(int j=0;j<sz;j++)
			{
				a[i][j]=dp.mat[i][j];
			}
		}
		tn=sz; n=2*tn;   init();
		ans=A^L;
		ULL sum2=0;
		for(int j=tn;j<n;j++)
		{
			sum2+=ans.mat[0][j];
		}
		printf("%I64u\n",(sum-sum2));
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值