• [JSOI2007]文本生成器(AC自动机)


    题意

    给定一些模式串,求长度为m的所有文本串的个数,且该文本串至少包括一个模式串,答案对10007取模

    思路

    //没有调用get_fail()调了一个小时我怕不是神仙....

    看到一堆字符串的匹配问题,首先就可以考虑自动机全家桶了......很容易发现用AC自动机看起来可做

    对所有串建AC自动机,然后变成trie图,一个文本串只要包括了一个end标记就说明包括至少一个串。在图上这样的统计问题,容易想到是dp

    于是大多数题解到这里就运用了一个经典的四字成语

    正难则反

    才怪

    虽然正难则反(所有串-不包含任一个的串)可做,但是按照正常的思路来说,则反首先要正难,然而它并不难......对于我这个蒟蒻来说很难就此想到反推(个人认为否定正着做会误导萌新比如本人)

    用f[ i ][ j ][0 / 1]表示到了i号点,当前字符串长度为j,是否经过过有标记的点,这样的总方案数,那么最终的答案就为sigma( f[ 0 ~ ndsum ][ m ][ 1 ] )。

    这样递推也是相当简单的,为了保证前一位先被求出来,所以外层循环先枚举字符串长度j

    if(is_end[now])//是终点就没有0状态 
    {
    	f[now][i][1]=(f[now][i][1]+f[j][i-1][0]+f[j][i-1][1])%mod;
    }
    else//不是终点 
    {
    	f[now][i][0]=(f[now][i][0]+f[j][i-1][0])%mod;
    	f[now][i][1]=(f[now][i][1]+f[j][i-1][1])%mod;
    	//f[now][i][1]不从f[j][i-1][0]转化过来qwq 
    }
    

    再加入AC自动机模板即可轻松切掉()

    Code:

    #include<bits/stdc++.h>
    #define N 6100
    using namespace std;
    const int mod = 10007;
    int n,m;
    int nxt[N][26],fail[N],is_end[N],ndsum;
    int f[N][105][2];//走了j步走到了i点,是否有经过单词 
    char s[N];
    void ad(char c[])
    {
    	int now=0,len=strlen(c);
    	for(int i=0;i<len;++i)
    	{
    		int p=c[i]-'A';
    		if(!nxt[now][p]) nxt[now][p]=++ndsum;
    		now=nxt[now][p];
    	}
    	is_end[now]=1;
    }
    void get_fail()
    {
    	queue<int> q;
    	for(int i=0;i<26;++i) if(nxt[0][i]) q.push(nxt[0][i]);
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int j=0;j<26;++j)
    		{
    			if(nxt[u][j])
    			{
    				q.push(nxt[u][j]);
    				fail[nxt[u][j]]=nxt[fail[u]][j];
    				is_end[nxt[u][j]]|=is_end[nxt[fail[u]][j]];
    			}
    			else nxt[u][j]=nxt[fail[u]][j];
    		}
    	}
    }
    void dp()
    {
    	f[0][0][0]=1;
    	for(int i=1;i<=m;++i)//step
    	{
    		for(int j=0;j<=ndsum;++j)//node
    		{
    			for(int k=0;k<26;++k)//next
    			{
    				int now=nxt[j][k];
    				if(is_end[now])//是终点就没有0状态 
    				{
    					f[now][i][1]=(f[now][i][1]+f[j][i-1][0]+f[j][i-1][1])%mod;
    				}
    				else//不是终点 
    				{
    					f[now][i][0]=(f[now][i][0]+f[j][i-1][0])%mod;
    					f[now][i][1]=(f[now][i][1]+f[j][i-1][1])%mod;
    					//f[now][i][1]不从f[j][i-1][0]转化过来qwq 
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",s);
    		ad(s);
    	}
    	get_fail();
    	dp();
    	int ans=0;
    	for(int i=0;i<=ndsum;++i) ans=(ans+f[i][m][1])%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (当然这道题f的第二维显然是可以滚掉的2333)

  • 相关阅读:
    Exception in thread "main" com.sun.xml.internal.ws.streaming.XMLStreamReaderException: unexpected XML tag.
    Navicat的快捷键
    win7虚拟机起不来,报错transport vmdb error -44 message the vmware authorization
    fedora19配置 SSH 免密码登陆
    Linux下Django的安装
    linux下为用户添加sudo命令功能
    ubuntu修改系统环境变量文件导致起不来
    fedorea19安装redis
    java下载csv文件,中文标题
    POJ A Simple Problem with Integers | 线段树基础练习
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11191137.html
Copyright © 2020-2023  润新知