zoukankan      html  css  js  c++  java
  • [JSOI2007]文本生成器(AC自动机+DP)

    题意

    给你n个串。问有多少长度为m的串使得这n个串至少在其中出现过一次。输出答案膜10007意义下的结果。

    (n<=100,每个串的长度<=100)

    题解

    在AC自动机上跑DP。

    用到一个容斥的思想,求至少出现过一次的次数就是,全部可能-一次都没出现的次数。

    所以考虑dp,对于一个长度为i的串从i-1转移,i位置上的数必须不是n个串中某个串的结尾。

    所以建立AC自动机,在上面跑。一个儿子从父亲转移时必须保证,儿子节点和儿子节点通过fail指针间接到达的点,都不是终止节点。

    具体看代码。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<cmath>
     5 #include<algorithm>
     6 #include<queue>
     7 using namespace std;
     8 const int mod=10007;
     9 const int N=7000;
    10 int n,m,tot,ans;
    11 char s[N];
    12 int dp[200][N];
    13 struct AC{
    14     int nxt[30],e,fail;
    15 }ac[N];
    16 void insert(char s[]){
    17 //    cout<<"asfsfsdfsdfs"<<endl;
    18     int len=strlen(s);
    19     int now=0;
    20     for(int i=0;i<len;i++){
    21         if(ac[now].nxt[s[i]-'A']==0)ac[now].nxt[s[i]-'A']=++tot;
    22         now=ac[now].nxt[s[i]-'A'];
    23     }
    24     ac[now].e=1;
    25 }
    26 void get_fail(){
    27     queue<int>q;
    28     for(int i=0;i<=25;i++){
    29         if(ac[0].nxt[i]){
    30         //    cout<<"sfsd"<<endl;
    31             q.push(ac[0].nxt[i]);
    32         }
    33     }
    34     while(!q.empty()){
    35         
    36         int u=q.front();//cout<<u<<endl;
    37         q.pop();
    38         for(int i=0;i<=25;i++){
    39             if(ac[u].nxt[i]){
    40                 ac[ac[u].nxt[i]].fail=ac[ac[u].fail].nxt[i];
    41                 ac[ac[u].nxt[i]].e|=ac[ac[ac[u].fail].nxt[i]].e;
    42                 q.push(ac[u].nxt[i]);
    43             }
    44             else ac[u].nxt[i]=ac[ac[u].fail].nxt[i];
    45         }
    46     }
    47 }
    48 int main(){
    49     scanf("%d%d",&n,&m);
    50     for(int i=1;i<=n;i++){
    51         scanf("%s",s);
    52         insert(s);
    53     }
    54     get_fail();
    55     dp[0][0]=1;
    56     for(int i=1;i<=m;i++)
    57         for(int j=0;j<=tot;j++)
    58             for(int x=0;x<=25;x++){
    59                 if(ac[ac[j].nxt[x]].e==0)dp[i][ac[j].nxt[x]]=(dp[i][ac[j].nxt[x]]+dp[i-1][j])%mod;
    60             }
    61     int ans=1;
    62     for(int i=1;i<=m;i++){
    63         ans*=26;
    64         ans%=mod;
    65     }
    66     for(int i=0;i<=tot;i++){
    67         ans-=dp[m][i];
    68         ans=(ans+mod)%mod;
    69     }
    70     printf("%d",ans);
    71     return 0;
    72 }
  • 相关阅读:
    动态规划精讲(一)53. 最大子序和
    ACM计算几何总结
    三角形外心的坐标公式
    三角形外心的坐标公式
    高精度模板
    位运算模板
    同余定理与逆元
    扩展欧几里得算法求二元一次方程
    1004. 最大连续1的个数 III
    剑指 Offer 04. 二维数组中的查找
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/9525060.html
Copyright © 2011-2022 走看看