首页 > 其他分享 >[HNOI2004] L 语言 题解(AC 自动机上 dp)

[HNOI2004] L 语言 题解(AC 自动机上 dp)

时间:2022-08-24 22:56:08浏览次数:88  
标签:AC ch 题解 HNOI2004 自动机 dp define

前言:

原版数据超弱,爆搜就能过(即洛谷里面 80 分的数据),在此不多说,这里讲的是正解。(如果不是正解我还敢写题解吗)

唔······话说洛谷里的题解用的都有状压,蒟蒻表示这题不用状压也能过(欢迎各位大佬 hack 我的做法,把我的做法弄到 TLE)。

正文:

令 \(s_i\) 表示文章的第 \(i\) 个字母,\(dp_i\) 表示文章前 \(i\) 位是否能被理解(为 \(1\) 表示能理解,否则不能)。那么如果 \(s_is_{i+1}···s_j\) 能匹配某个单词,且 \(dp_{i-1}=1\),则 \(dp_j=1\)。

但是数据范围不小,暴力枚举前面的那些字母去匹配是会超时的。看到多模式串,我们肯定得想到 AC 自动机喽。

然鹅······

大家可以看到,不动脑子地套上 AC 自动机后虽然比暴力多了 5 分,但还是不能通过全部数据。

我在交这份代码前也意识到了这点,所以又写了些优化,于是就过了。

讲下优化吧:

  1. 在算 \(dp_i\) 的时候,如果它已经可以为 \(1\),就跳出循环。

  2. 在算 \(dp_i\) 的时候,如果之前最多能匹配到的那一位离现在差的位数大于模式串长度的最大值,直接输出答案。举个例子,如今在算第 \(100\) 位的答案,但是当前最多只能匹配到第 \(10\) 位,那么就没必要继续算了,答案就取 \(10\)。因为你至少需要长度为 \(90\) 的模式串才可能让当前长度能匹配,但是找不到这么长的模式串,所以再往后就更不可能了。

  3. 在跳失配数组的时候,其中很多对答案都没有贡献(即不在叶子结点的),所以我们在求失配数组时,可以再开个数组,把实际该跳到哪里给记录下来,这个做法在 P3796 【模板】AC 自动机(加强版)中有人提及。不过我的代码经过实测,不加该优化也可以 AC,我下面放的代码也是没有这个优化的。当然,加上它应该会更快些。

Code:

#include<bits/stdc++.h>
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define inf 0x7fffffff
#define INF 0x3f
#define v e[i].y
using namespace std;
inline ll read(){
    char ch=getchar();ll x=0,w=1;
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-48,ch=getchar();return w==1?x:-x;
}
inline void write(ll x){
    if(x<0)x=-x,putchar('-');
    if(x<10){putchar(48+x);return;}
    write(x/10),putchar((x+10)%10+48);
}
int n=read(),m=read(),q[100005],ans,h,t,maxl;
bool dp[2000005];
char xk[2000005];
struct AC{
    int ch[30005][30],Fail[30005],val[30005],num;
    void Insert(int L){
        int op=0;
        for(int i=1,j;i<=L;i++){
            j=xk[i]-'a';
            op=(ch[op][j]?ch[op][j]:ch[op][j]=++num);
        }
        val[op]=L,maxl=max(maxl,L);
    }
    void getfail(){
        h=0,t=0;
        for(int i=0;i<26;i++)if(ch[0][i])q[++t]=ch[0][i],Fail[ch[0][i]]=0;
        while(h<t){
            int u=q[++h];
            for(int i=0;i<26;i++){
                if(ch[u][i])Fail[ch[u][i]]=ch[Fail[u]][i],q[++t]=ch[u][i];
                else ch[u][i]=ch[Fail[u]][i];
            }
        }
    }
    int solve(int L){
        for(int i=1;i<=L;i++)dp[i]=0;
        dp[0]=1,ans=0;
        int op=0;
        for(int i=1,j;i<=L;i++){
            if(ans+maxl<i)break;
            j=xk[i]-'a',op=ch[op][j];
            for(int k=op;k;k=Fail[k]){
                dp[i]|=dp[i-val[k]];
                if(dp[i])break;
            }
            if(dp[i])ans=i;
        }
        return ans;
    }
}A;
int main(){
    while(n--)scanf("%s",xk+1),A.Insert(strlen(xk+1));
    A.getfail();
    while(m--)scanf("%s",xk+1),write(A.solve(strlen(xk+1))),putchar(10);
    return 0;
}

标签:AC,ch,题解,HNOI2004,自动机,dp,define
From: https://www.cnblogs.com/mcDinic/p/16622565.html

相关文章

  • 【TPC附加赛YSTG】星坠比赛题解
    零、写在前面比赛地址本人比较菜,在这场接近提高组的模拟赛中获得了\(30+100+30+50=210\)的烂分事实上只要把暴力打足成绩一般就不会差但后来本人在Z......
  • LeetCode 重排链表算法题解 All In One
    LeetCode重排链表算法题解AllInOnejs/ts实现重排链表重排链表原理图解//快慢指针重排链表https://leetcode.com/problems/reorder-list/https://le......
  • SV中的包(Package)
    SystemVerilog中的包提供对许多不同数据类型、包括nets、variable、task、function、assertionsequences、property和checker声明的封装。封装再包内的数据可以在多个modu......
  • OpenStack发放云主机
    登陆网址用户名admin密码redhat具体安装步骤欢迎参照我的博客:https://www.cnblogs.com/kongshuo/p/16618008.html创建项目选择Identity创建项目创建用户并关......
  • ARC103E题解
    思路很奇怪(?)考虑是否合法的条件。注意到这个显然要求对称(即存在\(i\)必须存在\(n-i\)),如果不满足一定无解。然后比较显然的是\(1\)不存在和存在\(n\)都无解。然后......
  • ac 796子矩阵的和
    include<bits/stdc++.h>usingnamespacestd;intmain(){#ifdefONLINE_JUDGE#elsefreopen("D:\\Helloworld\\编程输入2\\in.tx......
  • 「AGC036F」Square Constraints 题解
    「AGC036F」SquareConstraints题解题目大意给定一个整数$n$,求有多少种$0\-\2n!-!1$的排列$P$,使得对于每个$i$,都有$n^2\lei^2+P_i^2\le4n^2$。......
  • ac 797 差分
    //常规时间复杂度为n*m//#include<bits/stdc++.h>//usingnamespacestd;//intmain(){//intn,m;//cin>>n>>m;//vectornums;//for......
  • 「POJ1475」Pushing Boxes 题解
    「POJ1475」PushingBoxes题解题目大意一张N行M列的地图,字符“.”表示空地,字符“#”表示墙,字符“S”表示人的起始位置,字符“B”表示箱子的起始位置,字符“T”表示箱子的......
  • 「COCI2014-2015#2」Norma 题解
    「COCI2014-2015#2」Norma题解题目大意给定一个\(n\)个数的序列\(a\),求\[\underset{i=1}{\overset{n}{\sum}}\underset{j=i}{\overset{n}{\sum}}(j-i+1)\min(a_i,a_......