【hdu2243】【AC自动机】【矩阵乘法】考研路茫茫——单词情结

首先利用集转化思想,将问题转化为求一个词根都不包含的单词有多少种。

可以想到将所有的词根建为AC自动,然后使用DP求解。

由于长度范围很大,可以使用矩阵乘法加速。

并且题目中要求的是“长度不超过L的”,所以要求sum = A ^ 1 + A ^ 2 + A ^ 3 + ...... + A ^ L

所有情况tot = 26 ^ 1 + 26 ^ 2 + 26 ^ 3 + ..... + 26 ^ L

后ans = tot - sum

代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 30; 
const int child_num = 26;
int matcal[maxn];
int sz,matnum;
long long n,k;
struct Matrix
{
	unsigned long long v[maxn][maxn];
	int x,y;
	Matrix()
	{
		memset(v,0,sizeof(v));
		x = y = 0;
	}
}mat;

class ACAutumaton
{
public:
	int chd[maxn][child_num];
	int fail[maxn],val[maxn];
	int Q[maxn],ID[128];
	int cnt;
	void Reset()
	{
		memset(chd[0],0,sizeof(chd[0]));
		memset(fail,0,sizeof(fail));
		for(int i = 0;i < 26;i++)ID['a'+i] = i;
		sz = 1;
	}
	void Insert(char *a,int key)
	{
		int p = 0;
		for(;*a; a++)
		{
			int c = ID[*a];
			if(!chd[p][c])
			{
				memset(chd[sz],0,sizeof(chd[sz]));
				val[sz] = 0;
				chd[p][c] = sz++;
			}
			p = chd[p][c];
		}
		val[p] = key;
	}
	void Construct()
	{
		int *s = Q,*e = Q;
		for(int i = 0;i < child_num;i++)
		{
			if(chd[0][i])
			{
				fail[ chd[0][i] ] = 0;
				*e ++ = chd[0][i];
			}
		}
		while(s != e)
		{
			int u = *s++;
			for(int i = 0;i < child_num;i++)
			{
				int &v = chd[u][i];
				if(v)
				{	
					*e ++ = v;
					fail[v] = chd[ fail[u] ][i];
					val[v] |= val[fail[v]];
				}
				else v = chd[ fail[u] ][i];
			}
		}
	}
}AC;
void init()
{
	freopen("hdu2243.in","r",stdin);
	freopen("hdu2243.out","w",stdout);
}

void work()
{
	memset(mat.v,0,sizeof(mat.v));
	memset(matcal,0,sizeof(matcal));
	matnum = 0;
	for(int i = 0;i < sz;i++)
		if(!AC.val[i])matcal[i] = ++matnum;
	mat.x = matnum;mat.y = matnum;
	for(int i = 0;i < sz;i++)
	{
		if(!AC.val[i])
		{
			for(int j = 0;j < child_num;j++)
			{
				if(!AC.val[AC.chd[i][j]])
					mat.v[matcal[i]][matcal[AC.chd[i][j]]]++;
			}
		}
	}
}

Matrix mtAdd(Matrix A,Matrix B)
{
	Matrix C;
	C.x = A.x;C.y = B.y;
	for(int i = 1;i <= A.x;i++)
	{
		for(int j = 1;j <= B.y;j++)
		{
			C.v[i][j] = A.v[i][j] + B.v[i][j];
		}
	}
	return C;
}

Matrix mtMul(Matrix A,Matrix B)
{
	if(!A.x || !A.y)return B;
	Matrix C;
	C.x = A.x;C.y = B.y;
	for(int i = 1;i <= A.x;i++)
	{
		for(int j = 1;j <= B.y;j++)
		{
			for(int k = 1;k <= A.y;k++)
			{
				C.v[i][j] += A.v[i][k] * B.v[k][j];
			}
		}
	}
	return C;
}

Matrix mtPow(Matrix A,long long b)
{
	Matrix tmp = A,ret;
	while(b)
	{
		if(b & 1)ret = mtMul(ret,tmp);
		tmp = mtMul(tmp,tmp);
		b >>= 1;
	}
	return ret;
}

Matrix mtCal(Matrix A,long long b)
{
	if(b == 1)return A;
	Matrix B = mtPow(A,(b + 1) / 2);
	Matrix C = mtCal(A,b / 2);
	if(b % 2 == 0)return mtAdd(mtMul(B,C),C);
	else return mtAdd(mtAdd(B,mtMul(C,B)),C);
}

unsigned long long quick_pow(long long a,long long b)
{
	unsigned long long tmp = a,ret = 1;
	while(b)
	{
		if(b & 1)ret = ret * tmp;
		tmp = tmp * tmp;
		b >>= 1;
	}
	return ret;
}

unsigned long long calc(int p,long long b)
{
	if(b == 1)return p;
	unsigned long long tmp1 = quick_pow(p,(b + 1) / 2);
	unsigned long long tmp2 = calc(p,b / 2);
	if(b % 2 == 0)return (tmp1 * tmp2) + tmp2;
	else return tmp1 + tmp2 + tmp1 * tmp2;
	
}

void solve()
{ 
	AC.Construct();
	work();
	Matrix tmp = mtCal(mat,k);
	unsigned long long tot = calc(26,k),ans = 0;
	for(int i = 1;i <= matnum;i++)
	{
		ans += tmp.v[1][i];
	}	
	printf("%llu\n",tot - ans);
}

void readdata()
{
	while(scanf("%lld%lld",&n,&k) != EOF)
	{
		AC.Reset();
		char s[10];
		for(int i = 1;i <= n;i++)
		{
			scanf("%s",s);
			AC.Insert(s,1);	
		}
		solve();
	}
}

int main()
{
	init();
	readdata();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值