zoukankan      html  css  js  c++  java
  • [JSOI2007]文本生成器

    [JSOI2007]文本生成器

    第一题自己没看题解就做出来的自动AC机题祭。

    一眼看出这道题正着求不好求,必须反过来,用总方案数-不合法方案数=合法方案数。

    任意一种不合法的方案,在AC自动机上(merge)时,所遍历到的每一个节点,必定都不是任何串的结尾节点,同时它在(fail)树上的祖先中也必然不包括结尾节点。(想一想,(fail)树就是前缀树,完整的串都匹配到了,前缀串还匹配不出来?)

    因此,我们定义布尔变量(ok),表示当前节点有没有直接或间接与结尾节点产生联系。

    初始化为(true)

    (build)时,有一些不同:

    while(!q.empty()){
    	int x=q.front();q.pop();
    	for(int i=0;i<26;i++){
    		if(t[x].ch[i])t[t[x].ch[i]].ok&=t[x].ok,t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]);
    		else t[x].ch[i]=t[t[x].fail].ch[i];
            }
    	t[x].ok&=t[t[x].fail].ok;
    }
    

    就是暴力更新(ok)

    然后就可以dp了。

    由于是在trie上dp,采取记忆化搜索可以明显减少难度。

    int dfs(int x,int len){
    	if(!t[x].ok)return 0;
    	if(!len)return 1;
    	if(f[x][len]!=-1)return f[x][len];
    	int res=0;
    	for(int i=0;i<26;i++)res=(res+dfs(t[x].ch[i],len-1))%mod;
    	return f[x][len]=res;
    }
    

    只要懂点dp的人就应该看得懂

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=10007;
    int n,m,cnt=1,S,f[6010][110],pov[110];
    char s[110];
    struct AC_Automaton{
    	int ch[26],fail;
    	bool ok;
    }t[6010];
    void ins(){
    	int x=1;
    	for(int i=0;i<S;i++){
    		if(!t[x].ch[s[i]-'A'])t[x].ch[s[i]-'A']=++cnt,t[cnt].ok=true;
    		x=t[x].ch[s[i]-'A'];
    	}
    	t[x].ok=false;
    }
    queue<int>q;
    void build(){
    	for(int i=0;i<26;i++){
    		if(!t[1].ch[i])t[1].ch[i]=1;
    		else t[t[1].ch[i]].fail=1,q.push(t[1].ch[i]);
    	}
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=0;i<26;i++){
    			if(t[x].ch[i])t[t[x].ch[i]].ok&=t[x].ok,t[t[x].ch[i]].fail=t[t[x].fail].ch[i],q.push(t[x].ch[i]);
    			else t[x].ch[i]=t[t[x].fail].ch[i];
    		}
    		t[x].ok&=t[t[x].fail].ok;
    	}
    }
    int dfs(int x,int len){
    	if(!t[x].ok)return 0;
    	if(!len)return 1;
    	if(f[x][len]!=-1)return f[x][len];
    	int res=0;
    	for(int i=0;i<26;i++)res=(res+dfs(t[x].ch[i],len-1))%mod;
    	return f[x][len]=res;
    }
    int main(){
    	scanf("%d%d",&n,&m),t[1].ok=true,memset(f,-1,sizeof(f)),pov[0]=1;
    	for(int i=1;i<=m;i++)pov[i]=(pov[i-1]*26)%mod;
    	for(int i=0;i<n;i++)scanf("%s",s),S=strlen(s),ins();
    	build();
    	printf("%d
    ",(pov[m]-dfs(1,m)+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    【反射】Java反射机制
    Composer教程之常用命令
    Composer教程之基础用法
    Composer教程之初识Composer
    Composer 的结构详解
    现代 PHP 新特性系列(七) —— 内置的 HTTP 服务器
    现代 PHP 新特性系列(一) —— 命名空间
    现代 PHP 新特性系列(二) —— 善用接口
    现代 PHP 新特性系列(三) —— Trait 概览
    现代 PHP 新特性系列(四) —— 生成器的创建和使用
  • 原文地址:https://www.cnblogs.com/Troverld/p/12781141.html
Copyright © 2011-2022 走看看