zoukankan      html  css  js  c++  java
  • 【*篇】SPOJ GEN Text Generator AC自动机+矩阵快速幂

    还有5天省选才开始点字符串这棵技能树是不是太晚了点...

    题目の传送门

    AC自动机不想讲了QAQ.其实很久以前是学过然后打过板子的, 但也仅限于打过板子了~
    之前莫名其妙学了一个指针版的但是好像不能用循环遍历fail好像就啥也干不了于是改成了数组...)
    其实就是Trie树上挂fail指针... 然后可以完成多串的kmp的样子...

    直接看题吧. 题目大意: 求长度为(L)的,包含给定的(n)个短串中的至少一个的字符串的数量.

    考虑补集转化, 考虑含这些短串的字符串的数量. 然后用所有长度为(L)的字符串((26^L))的数量减掉就行了.
    然后建立AC自动机, 对于不存在的节点我们就直接让它指向根就可以了.
    (f[i][j])表示在trie树上从(i)一步(j)不经过有isend标记的节点的方案数, 我们遍历AC自动机就可以求出这个矩阵(算是临接矩阵咯~).
    然后根据临接矩阵的特点, 我们求出(f^L[i][j])就是从(i)开始走(L)步到(j)时合法的方案数.
    然后我们最后要求的数量就是(ans=sum_{i=1}^tf[root][i]), 其中(t)表示Trie树上的节点数, 最后我们用(26^L-ans)即可.
    有一个优化就是如果一个点的fail指针指向了一个有isend标记的节点, 那这个点也是不可达的(由fail指针的定义显然), 我们就也把它贴上isend标记即可.

    下面就是隆重登场的1A代码:
    其实我的spoj账号因为之前测试自动提交脚本的缘故已经惨不忍睹了..然而一大部分都已经被我disqualify掉了2333~

    #include <queue>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N=66;
    const int p=10007;
    char c[10];
    int ch[N][26],fail[N],tot;
    bool isend[N];
    struct mat{int ma[N][N];}a;
    inline void init(){
    	memset(ch,0,sizeof ch); tot=0;
    	memset(fail,0,sizeof fail);
    	memset(isend,0,sizeof isend);
    	memset(a.ma,0,sizeof a.ma);
    }
    inline void inser(char* s){
    	int len=strlen(s),now=0;
    	for(int i=0;i<len;++i){
    		int k=s[i]-'A';
    		if(!ch[now][k]) ch[now][k]=++tot;
    		now=ch[now][k];
    	} isend[now]=1;
    }
    inline void make_fail(){
    	queue<int> q;
    	for(int i=0;i<26;++i)
    		if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0;
    	while(!q.empty()){
    		int now=q.front(); q.pop();
    		if(isend[fail[now]]) isend[now]=1;
    		for(int i=0;i<26;++i){
    			if(ch[now][i]) fail[ch[now][i]]=ch[fail[now]][i],q.push(ch[now][i]);
    			else ch[now][i]=ch[fail[now]][i];
    		}
    	}
    }
    inline void make_mat(){
    	for(int i=0;i<=tot;++i)
    		for(int j=0;j<26;++j)
    			if(!isend[ch[i][j]])
    				++a.ma[i][ch[i][j]];
    }
    mat mat_mul(mat a,mat b){ mat c; int ans;
    	for(int i=0;i<=tot;++i)
    		for(int j=0;j<=tot;++j){
    			ans=0;
    			for(int k=0;k<=tot;++k){
    				ans+=a.ma[i][k]*b.ma[k][j];
    				while(ans>=p) ans-=p;
    			}
    			c.ma[i][j]=ans;
    		}
    	return c;
    }
    inline mat mat_pow(mat a,int b){ mat s=a;
    	for(--b;b;b>>=1,a=mat_mul(a,a))
    		if(b&1) s=mat_mul(s,a);
    	return s;
    }
    inline int qpow(int a,int b,int s=1){
    	for(a%=p;b;b>>=1,a=a*a%p)
    		if(b&1) s=s*a%p;
    	return s;
    }
    int main(){ int n,l;
    	while(scanf("%d%d",&n,&l)!=EOF){
    		init();
    		for(int i=0;i<n;++i){
    			scanf("%s",c); inser(c);
    		} make_fail();
    		make_mat();
    		mat b=mat_pow(a,l);
    		int ans=qpow(26,l);
    		for(int i=0;i<tot;++i){
    			ans-=b.ma[0][i];
    			if(ans<0) ans=ans+p;
    		}
    		printf("%d
    ",ans%p);
    	}
    }
    
  • 相关阅读:
    Java从入门到实战之(22)数组之练习
    LeetCode343. 整数拆分
    LeetCode64. 最小路径和
    LeetCode120. 三角形最小路径和
    LeetCode37. 解数独
    实验:通过Telnet访问路由器
    telnet 命令使用方法详解,telnet命令怎么用?
    COBIT、ITIL
    500 internal privoxy error错误怎么解决?
    iPhone12有充电器和耳机吗
  • 原文地址:https://www.cnblogs.com/enzymii/p/8682830.html
Copyright © 2011-2022 走看看