bzoj 4460 : [Jsoi2013]广告计划

脑残一下午
首先,对于所有字符串建立后缀自动机
这里可以是广义后缀自动机或者狭义后缀自动机,我会给出2份代码
然后是匹配时间
我们枚举答案,问题变成一个答案是否能够被成功匹配
观察发现,如果当前答案为 ans ,那么 11+ans1+ans2...... 都会在一排一起出现
同理 22+ans2+ans2...... 也会这样
我们定义 have[i][j] 表示 ii+ansi+ans2...... 是否在第j列出现
这个可以在后缀自动机上求 right 集合实现
那么ans什么情况会成为答案呢?
我们观察样例, have[1][2]have[2][3]have[3][3] 同时存在的时候, ans 为答案
结论: ans 为答案,仅当存在一个 x ,使得have[1][x]have[2][x]have[3][x]....have[ans][x]同时存在,但是存在例外: x 有一次机会在中间某个地方+1
然后暴力跑就好了,由于我复杂度没有卡满所以跑得飞快QAQ
顺便说一句,两份代码统计 right 的方法是一样的,可以互换,但是有基数排序的方法
代码
狭义后缀自动机

#include<bits/stdc++.h>
#define N 20005
using namespace std;
namespace sam{
    int last=1,tot=1;
    int t[N][27],pre[N],val[N],deep[N];
    bitset<102>right[N];
    void insert(int x,int ps){
        int np=++tot,now=last;last=np;val[tot]=1;
        deep[np]=deep[now]+1;right[np][ps]=1;
        while(now&&!t[now][x]){
            t[now][x]=np;now=pre[now];
        }
        if(!now)pre[np]=1;
        else{
            int q=t[now][x],p=now;
            if(deep[p]+1==deep[q])pre[np]=q;
            else{
                int nq=++tot;deep[nq]=deep[p]+1;
                pre[nq]=pre[q];pre[np]=pre[q]=nq;
                memcpy(t[nq],t[q],sizeof t[q]);right[nq]=right[q];
                while(now&&t[now][x]==q)t[now][x]=nq,now=pre[now];
            }
        }
    }
    struct node{
        int id,x;
        friend bool operator < (const node &a,const node &b){
            return a.x<b.x;
        }
    }s[N];
    void add(){
        for(int i=1;i<=tot;i++)s[i]=(node){i,deep[i]};
        sort(s+1,s+tot+1);
        for(int i=tot;i>=2;i--){
            right[pre[s[i].id]]|=right[s[i].id];
        }
    }
}
char p[205];
int n,m,len;
bitset<102>have[205];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",p+1);
        for(int j=1;j<=m;j++){
            sam::insert(p[j]-'a',j);
        }
        sam::insert(26,0);
    }
    sam::add();
    scanf("%s",p+1);len=strlen(p+1);
    for(int ans=1;ans<=len;ans++){
        for(int j=1;j<=ans;j++){
            int x=1,y=j,f=0;
            while(y<=len){
                x=sam::t[x][p[y]-'a'];
                y=y+ans;f++;
            }
            have[j]=sam::right[x]>>(f-1);
        }
        for(int i=1;i<=m;i++){
            if(!have[1][i])continue;
            int a=1,b=1;
            for(int j=2;j<=ans;j++){
                b=(a|b)&&have[j][i+1];
                a&=have[j][i];
                if((!a)&&(!b))break;
            }
            if(a|b)cout<<ans<<endl,exit(0);
        }
    }
}

广义后缀自动机

#include<bits/stdc++.h>
#define N 20005
using namespace std;
namespace sam{
    int last=1,tot=1;
    int t[N][27],pre[N],val[N],deep[N];
    bitset<102>right[N];
    void insert(int x,int ps){
        if(t[last][x]){
            last=t[last][x];right[x][ps]=1;return;
        }
        int np=++tot,now=last;last=np;val[tot]=1;
        deep[np]=deep[now]+1;right[np][ps]=1;
        while(now&&!t[now][x]){
            t[now][x]=np;now=pre[now];
        }
        if(!now)pre[np]=1;
        else{
            int q=t[now][x],p=now;
            if(deep[p]+1==deep[q])pre[np]=q;
            else{
                int nq=++tot;deep[nq]=deep[p]+1;
                pre[nq]=pre[q];pre[np]=pre[q]=nq;
                memcpy(t[nq],t[q],sizeof t[q]);right[nq]=right[q];
                while(now&&t[now][x]==q)t[now][x]=nq,now=pre[now];
            }
        }
    }
    int nxt[N],fir[N],to[N],Sz;
    void add(int x,int y){
        nxt[++Sz]=fir[x];fir[x]=Sz;to[Sz]=y;
    }
    void dfs(int x){
        for(int u=fir[x];u;u=nxt[u]){
            dfs(to[u]);right[x]|=right[to[u]];
        }
    }
    void add(){
        for(int i=2;i<=tot;i++)add(pre[i],i);
        dfs(1);
    }
}
char p[205];
int n,m,len;
bitset<102>have[205];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",p+1);
        for(int j=1;j<=m;j++){
            sam::insert(p[j]-'a',j);
        }
        sam::last=1;
    }
    sam::add();
    scanf("%s",p+1);len=strlen(p+1);
    for(int ans=1;ans<=len;ans++){
        for(int j=1;j<=ans;j++){
            int x=1,y=j,f=0;
            while(y<=len){
                x=sam::t[x][p[y]-'a'];
                y=y+ans;f++;
            }
            have[j]=sam::right[x]>>(f-1);
        }
        for(int i=1;i<=m;i++){
            if(!have[1][i])continue;
            int a=1,b=1;
            for(int j=2;j<=ans;j++){
                b=(a|b)&&have[j][i+1];
                a&=have[j][i];
                if((!a)&&(!b))break;
            }
            if(a|b)cout<<ans<<endl,exit(0);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值