zoukankan      html  css  js  c++  java
  • HDU. 2243. 考研路茫茫——单词情结(AC自动机 DP 矩阵快速幂)

    题目链接

    作业里的水题拿来水水(AC自动机都忘了)


    (Description)
    给定(n)个串和(L),求长度不超过(L)且至少含有(n)个串中的一个的字符串有多少个。
    (nleq 5, Llt 2^{31}, 串长leq 5)。字符集为小写字母。

    (Solution)
    其实就是[JSOI2007]文本生成器加强版。
    (n)个串建AC自动机。
    考虑串长较短时,令(f[i][j])表示当前构造了(i)个字符,匹配位置在自动机的(j)节点。
    则:

    [f[i][j]=sum_{kin pre[j]}f[i-1][k]+26cdot f[i-1][j]cdot ig[end[j]=1ig] ]

    其中(pre[j])表示(son)连向(j)节点的节点集合,(end[j])表示(j)是否是(n)个串中某个串的终止节点。都在建AC自动机时处理。
    答案就是(sum_{i=1}^{L}sum_{end[j]=1}f[i][j])

    因为DP是与(i)无关的线性递推,所以可以矩阵快速幂。
    因为要对(i)求和,所以矩阵加一行表示对(f[i-1][j])(sum_{i-1})的求和(注意这一行只有(end[j]=1)的位置才是(1))。
    构造出((tot+1)cdot(tot+1))的转移矩阵,快速幂即可((tot)为AC自动机节点数)。
    复杂度(O(n^3log L))(n)为AC自动机节点数(25)

    同样可以先算不合法的方案数(DP式子稍微变一下,矩阵快速幂),用总方案数(sum_{i=1}^L26^i)减掉(同样可以矩阵快速幂,或者Exgcd求(2^{64})逆元)。


    //15MS	1508K
    #include <bits/stdc++.h>
    #define pc putchar
    #define gc() getchar()
    #define pb emplace_back
    typedef unsigned long long ull;
    const int N=55,S=26;
    
    ull f[N*10][N];
    char s[N];
    struct AC_Automaton
    {
    	int tot,q[N],end[N],fail[N],son[N][S];
    	std::vector<int> pre[N];
    	void Init()
    	{
    		memset(end,0,tot+1<<2);
    		memset(son,0,(tot+1)*S*4);
    		for(int i=0; i<=tot; ++i) pre[i].clear();
    		tot=0;
    	}
    	void Insert(char *s)
    	{
    		int l=strlen(s+1),u=0;
    		for(int i=1,c; i<=l; ++i)
    		{
    			if(!son[u][c=s[i]-'a']) son[u][c]=++tot;
    			u=son[u][c];
    		}
    		end[u]=1;
    	}
    	void Build()
    	{
    		int h=0,t=0;
    		for(int i=0; i<S; ++i)
    			if(son[0][i]) fail[son[0][i]]=0, q[t++]=son[0][i];
    		while(h<t)
    		{
    			int x=q[h++];
    			end[x]|=end[fail[x]];
    			for(int v,i=0; i<S; ++i)
    				if(son[x][i])
    					fail[v=son[x][i]]=son[fail[x]][i], q[t++]=v;
    				else son[x][i]=son[fail[x]][i];
    		}
    		for(int i=0; i<=tot; ++i)
    			if(!end[i])
    				for(int j=0; j<S; ++j)
    					pre[son[i][j]].pb(i);
    	}
    }ac;
    
    struct Matrix
    {
    	int n;
    	ull a[N][N];
    	void Init(int _n)
    	{
    		n=_n;
    		memset(a,0,sizeof a);
    	}
    	Matrix operator *(const Matrix &x)
    	{
    		Matrix res; res.n=n;
    		for(int i=0; i<n; ++i)
    			for(int j=0; j<n; ++j)
    			{
    				ull t=0;
    				for(int k=0; k<n; ++k)
    					t+=a[i][k]*x.a[k][j];
    				res.a[i][j]=t;
    			}
    		return res;
    	}
    };
    
    Matrix FP(Matrix x,int k)
    {
    	Matrix t=x; --k;
    	for(; k; k>>=1,x=x*x)
    		if(k&1) t=t*x;
    	return t;
    }
    
    int main()
    {
    	int n,L;
    	while(~scanf("%d%d",&n,&L))
    	{
    		ac.Init();
    		while(n--) scanf("%s",s+1), ac.Insert(s);
    		ac.Build();
    
    //Brute Force
    //		memset(f,0,sizeof f);
    //		f[0][0]=1;
    //		int tot=ac.tot;
    //		for(int i=1; i<=L; ++i)
    //			for(int j=0; j<=tot; ++j)
    //			{
    //				if(ac.end[j]) f[i][j]=f[i-1][j]*26;
    //				for(auto v:ac.pre[j])
    //					f[i][j]+=f[i-1][v];
    //			}
    //		ull Ans=0;
    //		for(int i=1; i<=L; ++i)
    //			for(int j=0; j<=tot; ++j)
    //				if(ac.end[j]) Ans+=f[i][j];
    //		printf("%llu
    
    ",Ans);
    
    		Matrix A; A.Init(ac.tot+2);
    		int n=ac.tot+1;
    		for(int i=0; i<n; ++i)
    		{
    			if(ac.end[i]) A.a[i][i]=26;
    			for(auto v:ac.pre[i]) ++A.a[i][v];
    		}
    		for(int i=0; i<n; ++i)
    			if(ac.end[i]) A.a[n][i]=1;
    		A.a[n][n]=1;
    		A=FP(A,L);
    
    		ull res=A.a[n][0];
    		for(int i=0; i<n; ++i) if(ac.end[i]) res+=A.a[i][0];
    		printf("%llu
    ",res);
    	}
    
    	return 0;
    }
    
    ------------------------------------------------------------------------------------------------------------------------
    无心插柳柳成荫才是美丽
    有哪种美好会来自于刻意
    这一生波澜壮阔或是不惊都没问题
    只愿你能够拥抱那种美丽
    ------------------------------------------------------------------------------------------------------------------------
  • 相关阅读:
    常用算法之选择排序
    常用算法之插入排序
    常用算法之冒泡排序
    Python hashlib模块 (主要记录md5加密)
    Django Model
    CSS实现table td中文字的省略与显示
    JS读取文件,Javascript之文件操作 (IE)
    ie6789和其他浏览器之间的鼠标左、中、右键的event.button不一致的办法
    兼容和样式
    kindeditor的docs
  • 原文地址:https://www.cnblogs.com/SovietPower/p/14797089.html
Copyright © 2011-2022 走看看