• BZOJ_1030_[JSOI2007]_文本生成器_(AC自动机+DP)


    描述


    http://www.lydsy.com/JudgeOnline/problem.php?id=1030

    给出一些单词,问长度为(m)的文章有多少文章中出现过任意一个或多个单词.

    分析


    文章总数为(26^m),减去没有出现过任意单词的文章数量就是答案.

    那么如何求"没有出现过任意单词的文章的数量"呢?

    我们用所有单词建立一个AC自动机,那么问题就转化成了在AC自动机上跑(m)步(相当于边跑边枚举),不经过单词节点(相当于没有单词成功匹配).在AC自动机上dp即可.

    (dp[i][j])表示文章的第(i)个字母在自动机的(j)号节点上的方案数.那么最终答案就是(sum_{i=0}^{sz}dp[m][i]).

    注意(i)是从0开始的,因为当走到AC自动机上没有的节点时就会走到0节点.

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn=6000+5,maxm=100+5,type=26,mod=10007;
     5 int n,m,a=1,b;
     6 int dp[maxm][maxn];
     7 char s[maxm];
     8 struct Aho_Corasick{
     9     int sz,ch[maxn][type],f[maxn],q[maxn];
    10     bool val[maxn];
    11     Aho_Corasick(){ memset(val,false,sizeof val); }
    12     inline void ins(char *s){
    13         int u=0,m=strlen(s+1);
    14         for(int i=1;i<=m;i++){
    15             int c=s[i]-'A';
    16             if(!ch[u][c]) u=ch[u][c]=++sz;
    17             else u=ch[u][c];
    18         }
    19         val[u]=true;
    20     }
    21     inline void get_fail(){
    22         int L=1,R=0;
    23         for(int c=0;c<type;c++){
    24             int u=ch[0][c];
    25             if(u){ f[u]=0;q[++R]=u; }
    26         }
    27         while(L<=R){
    28             int u=q[L++];
    29             for(int c=0;c<type;c++){
    30                 if(!ch[u][c]){ ch[u][c]=ch[f[u]][c]; continue; }
    31                 f[ch[u][c]]=ch[f[u]][c];
    32                 if(val[ch[f[u]][c]]) val[ch[u][c]]=true;
    33                 q[++R]=ch[u][c];
    34             }
    35         }
    36     }
    37     inline void DP(int x){
    38         for(int i=0;i<=sz;i++){
    39             if(val[i]||!dp[x-1][i]) continue;
    40             for(int j=0;j<type;j++){
    41                 if(!val[ch[i][j]]) dp[x][ch[i][j]]=(dp[x][ch[i][j]]+dp[x-1][i])%mod;
    42             }
    43         }
    44     }
    45 }ac;
    46 int main(){
    47     scanf("%d%d",&n,&m);
    48     for(int i=1;i<=n;i++){
    49         scanf("%s",s+1);
    50         ac.ins(s);
    51     }
    52     ac.get_fail();
    53     dp[0][0]=1;
    54     for(int i=1;i<=m;i++) ac.DP(i);
    55     for(int i=1;i<=m;i++) a=(a*type)%mod;
    56     for(int i=0;i<=ac.sz;i++) b=(b+dp[m][i])%mod;
    57     printf("%d
    ",(a-b+mod)%mod);
    58     return 0;
    59 }
    View Code

    1030: [JSOI2007]文本生成器

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 3588  Solved: 1463
    [Submit][Status][Discuss]

    Description

      JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,
    他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文
    章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,
    那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的
    标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
    生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?

    Input

      输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<= 60),GW文本生成器 v6生成的文本固
    定长度M;以下N行,每一行包含一个使用者了解的单词。这里所有单词及文本的长度不会超过100,并且只可能包
    含英文大写字母A..Z

    Output

      一个整数,表示可能的文章总数。只需要知道结果模10007的值。

    Sample Input

    2 2
    A
    B

    Sample Output

    100

    HINT

    Source

  • 相关阅读:
    combination sum II
    Combination sum
    Swap Nodes in Pairs(交换节点)
    4 sum
    3 sum closest
    五大常用算法:分治、动态规划、贪心、回溯和分支界定
    3sum(从数组中找出三个数的和为0)
    从系统相册选择照片时,没有选框,相册无选框
    iOS Xcode 调试技巧 全局断点这样加才有意思
    将任意对象存进数据库
  • 原文地址:https://www.cnblogs.com/Sunnie69/p/5651505.html
Copyright © 2020-2023  润新知