zoukankan      html  css  js  c++  java
  • 题解 lg4052 [JSOI2007]文本生成器--AC自动机

    题目链接

    这是一道十分经典的AC自动机+dp的题目

    大意

    求长度为m、字符集为大写字母、包含至少一个模式串的所有字符串的数目,对(1e4+7)取模

    思路

    看到这种匹配类的东西可以考虑AC自动机。但直接求出答案比较复杂,我们考虑转换为其补集即不包含任何任何一个模式串的字符串数目.

    对模式串建立AC自动机,当节点为 危险节点 ,即结束节点或者其祖先为结束节点(因为一个单词的后缀是一个可读单词时,这个单词一定可读的)都不可以,所以对于后一种有以下代码

    ed[trie[x][i]]|=ed[fail[trie[x][i]]];
    

    假设 (f_{i,j}) 表示已经匹配到(trie)图上的节点(i),长度为 (j) 的不合法字符串个数,我们枚举接下来的字符,即可实现转移,有以下dp方程式

    [f_{trie_{i,k},j}(trie_{i,k} otin ed)=sum_{i otin ed} f_{trie_{i},j-1} ]

    意思就是说将这个非危险节点的答案转移到它的非危险的儿子上去.

    注意(我掉的坑)

    1. (Trie)的大小为字符串总大小×字符集大小

      不会吧不会吧,真有人这么傻掉这个坑吗

      就是我啊

    2. 根节点在AC自动机中有它特有的含义,仔细思考

    代码

    #include<bits/stdc++.h>
    using namespace std;
    int const MOD=1e4+7;
    int n,m,sum;
    int f[6001][1010];
    char s[2000];
    struct AC_Automaton{
    	int trie[10010][30],fail[60010],ed[60010];
    	int tot;
    	queue<int>q;
    	void insert(){
    		int len=strlen(s+1);int now=0;
    		for(int i=1;i<=len;i++){
    			int x=s[i]-'A';
    			if(!trie[now][x]){trie[now][x]=++tot;}
    			now=trie[now][x];
    		}
    		ed[now]=1;
    	}
    	void getfail(){
    		for(int i=0;i<26;i++){
    			if(trie[0][i]){
    				fail[trie[0][i]]=0;
    				q.push(trie[0][i]);
    			}
    		}
    		while(!q.empty()){
    			int x=q.front();q.pop();
    			for(int i=0;i<26;i++){
    				if(trie[x][i]){
    					fail[trie[x][i]]=trie[fail[x]][i];
    					q.push(trie[x][i]);
    					ed[trie[x][i]]|=ed[fail[trie[x][i]]];
    				}else{
    					trie[x][i]=trie[fail[x]][i];
    				}
    			}
    		}
    	}
    }ACA;
    int qpow(int x,int k){
    	int ans=1;
    	while(k){
    		if(k&1)ans=(ans*x)%MOD;
    		k>>=1;
    		x=x*x%MOD;
    	}
    	return ans;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%s",s+1);
    		ACA.insert();
    	}
    	ACA.getfail();
    	for(int i=0;i<26;i++){
    		if(!ACA.ed[ACA.trie[0][i]])f[ACA.trie[0][i]][1]+=1;
    	}
    	for(int i=2;i<=m;i++){
    		for(int j=0;j<=ACA.tot;j++){
    			if(!ACA.ed[j]){
    				for(int k=0;k<26;k++){
    					if(!ACA.ed[ACA.trie[j][k]])f[ACA.trie[j][k]][i]=(f[ACA.trie[j][k]][i]+f[j][i-1])%MOD;
    				}
    			}
    		}
    	}
    	for(int j=0;j<=ACA.tot;j++)if(!ACA.ed[j])sum=(sum+f[j][m])%MOD;
    	printf("%d
    ",(qpow(26,m)-sum+MOD)%MOD);
    	return 0;
    }
    
  • 相关阅读:
    [导入]如何使用C#调用非托管DLL函数
    [导入]ASP.NET 2.0 Page的执行顺序
    [导入]C#实现捕获当前屏幕截图(转)
    初学者入门经典:Java环境配置大全
    [导入]使用母版页时内容页如何使用css和javascript(转)
    CruiseControl.NET配置总结
    使用SQL语句清空数据库所有表的数据
    运行时修改config文件
    [导入]Repater 控件的应用(学习)
    [导入]PictureBox 读取图片及绘画
  • 原文地址:https://www.cnblogs.com/fpjo/p/13886723.html
Copyright © 2011-2022 走看看