【JZOJ5098】【GDOI2017 day1】微信

本文介绍如何使用SAM广义后缀自动机解决字符串匹配问题,通过构建Trie树和SAM,实现高效的字符串查询。算法复杂度为O(2^N*N),支持O(1)时间复杂度的查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们可以建出一颗trie,在trie上建广义后缀自动机sam,在sam上打好二进制标记,由于sam中fa[x]与x的关系是fa[x]的right集包含x的right集,所以我们可以将x的二进制与fa[x]or一下并更新答案,最后我们还可以发现一个二进制的答按肯定没有它的子集劣,O(2^N*N)下传一下即可,对于询问O(1)输出。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2e6+5;
struct code{
    int len,a[26],fa,bz;
}f[maxn];
int n,m,i,t,j,k,q,nq,l,x,y,z,g[maxn],p[maxn],num,v[maxn],b[maxn],er[21],bz[maxn],g1[maxn/2][26],fa[maxn];
char s[maxn/10];
void insert(int np,int p,int x,int y){
    f[np].len=f[p].len+1;f[np].bz=y;
    while (!f[p].a[x] && p) f[p].a[x]=np,p=f[p].fa;
    if (!p) f[np].fa=1;
    else{q=f[p].a[x];
        if (f[q].len==f[p].len+1) f[np].fa=q;
        else{
            nq=++num;f[nq]=f[q];f[q].fa=f[np].fa=nq;f[nq].len=f[p].len+1;
            while (f[p].a[x]==q) f[p].a[x]=nq,p=f[p].fa;
        }
    }
}
void bfs(){
    int i=0,j=1;
    while (i<j){
        x=v[++i];
        for (k=0;k<26;k++){
            y=g1[x][k];
            if (!y) continue;
            p[y]=++num,insert(p[y],p[x],k,bz[y]);v[++j]=y;
        }
    }
}
int main(){
    freopen("wechat.in","r",stdin);freopen("wechat.out","w",stdout);
    scanf("%d\n",&n);er[1]=1;
    for (i=2;i<=n;i++)er[i]=er[i-1]*2;
    for (i=1;i<=n;i++){
        scanf("%s\n",s+1);t=strlen(s+1);x=0;
        for (j=1;j<=t;j++){
            if (s[j]=='<'){
                x=fa[x];continue;
            }
            if (!g1[x][s[j]-97]) g1[x][s[j]-97]=++num,fa[num]=x;
            x=g1[x][s[j]-97];bz[x]|=er[i];
        }
    }num=p[0]=1;
    bfs();
    for (i=1;i<=num;i++) b[f[i].len]++;
    for (i=1;i<=num;i++) b[i]+=b[i-1];
    for (i=1;i<=num;i++) v[b[f[i].len]--]=i;
    for (i=num;i>=1;i--){
        x=v[i];y=f[x].fa;f[y].bz=(f[x].bz|f[y].bz);
        g[f[x].bz]=max(g[f[x].bz],f[x].len);
    }q=(1<<n)-1;
    for (i=q;i>=1;i--)
        for (j=1;j<=n;j++)
            if (er[j]&i)g[i-er[j]]=max(g[i-er[j]],g[i]);
    scanf("%d\n",&m);
    for (i=1;i<=m;i++){
        scanf("%s\n",s+1);x=0;
        for (j=1;j<=n;j++)
            x+=er[j]*(s[j]-48);
        printf("%d\n",g[x]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值