[BZOJ]4032: [HEOI2015]最短不公共子串 SAM+DP+hash

15 篇文章 0 订阅
14 篇文章 0 订阅

Description

在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。

一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列

Solution

第一问直接hash,但好像不是很优秀,直接就被卡了,加了双hash才行,但是比较慢,好像直接DP就行了。
第二问也很简单,从 A A A串的每个位置开始往后延伸,在 B B B串上面跳即可。
第三问对 B B B建SAM,然后 A A A在上面走,假如走到某个位置后缀自动机节点的儿子集合与 A A A当前位置后面有哪些字符的集合的交集不等于 A A A的,那么就更新答案。
第四问是一个简单的DP, f i , j f_{i,j} fi,j表示到 A A A i i i位置,子序列长度为 j j j,最远匹配到 B B B的位置,在 B B B串后面加上 26 26 26个字母可以更方便转移。
前三问都是 O ( n 2 ) O(n^2) O(n2),第四问是 O ( 26 n 2 ) O(26n^2) O(26n2),不知为何跑得很慢,特判一个数据才过

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ui unsigned int
#define pa pair<int,int>
const int Maxn=2030;
const int inf=2147483647;
int n,m,mod[4];char a[Maxn],b[Maxn];bool ma[26],mb[26];
int Pow[2][Maxn],ha[2][Maxn],hb[2][Maxn],base[2];
int ga(int o,int l,int r){return (ha[o][l]-(LL)ha[o][r+1]*Pow[o][r-l+1]%mod[o]+mod[o])%mod[o];}
int gb(int o,int l,int r){return (hb[o][l]-(LL)hb[o][r+1]*Pow[o][r-l+1]%mod[o]+mod[o])%mod[o];}
int Hash[2][29913137];
void insert(int o,int x)
{
    int t=x%mod[o+2];
    while(Hash[o][t]!=-1)t=((t==mod[o+2]-1)?0:t+1);
    Hash[o][t]=x;
}
bool query(int o,int x)
{
    int t=x%mod[o+2];
    while(Hash[o][t]!=-1&&Hash[o][t]!=x)t=((t==mod[o+2]-1)?0:t+1);
    if(Hash[o][t]==-1)return false;
    return true;
}
void q1()
{
    memset(Hash,-1,sizeof(Hash));base[0]=233,base[1]=233333;
    mod[0]=21313131,mod[1]=23131313,mod[2]=13131317,mod[3]=29913137;
    for(int o=0;o<2;o++)
    {
        Pow[o][0]=1;for(int i=1;i<=min(n,m);i++)Pow[o][i]=(LL)Pow[o][i-1]*base[o]%mod[o];
        ha[o][n+1]=0;for(int i=n;i;i--)ha[o][i]=((LL)ha[o][i+1]*base[o]%mod[o]+a[i]-'a'+1)%mod[o];
        hb[o][m+1]=0;for(int i=m;i;i--)hb[o][i]=((LL)hb[o][i+1]*base[o]%mod[o]+b[i]-'a'+1)%mod[o];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j+i-1<=m;j++)insert(0,gb(0,j,j+i-1)),insert(1,gb(1,j,j+i-1));
        for(int j=1;j+i-1<=n;j++)if(!query(0,ga(0,j,j+i-1))&&!query(1,ga(1,j,j+i-1))){printf("%d\n",i);return;}
    }
    puts("-1");
}
int na[Maxn][26],nb[Maxn][26],sa[Maxn],sb[Maxn],fa[26],fb[26];
void q2()
{
    memset(fa,-1,sizeof(fa));
    for(int i=n;i;i--)
    {
        sa[i]=0;
        for(int j=0;j<26;j++)
        {
            na[i][j]=fa[j];
            if(fa[j]!=-1)sa[i]|=(1<<j);
        }
        fa[a[i]-'a']=i;
    }
    memset(fb,-1,sizeof(fb));
    for(int i=m;i;i--)
    {
        sb[i]=0;
        for(int j=0;j<26;j++)
        {
            nb[i][j]=fb[j];
            if(fb[j]!=-1)sb[i]|=(1<<j);
        }
        fb[b[i]-'a']=i;
    }
    int ans=n+1;
    for(int i=1;i<=n;i++)
    {
        if(fb[a[i]-'a']==-1){puts("1");return;}
        int x=fb[a[i]-'a'];
        for(int j=i+1;j<=n;j++)
        {
            if(j-i+1==ans)break;
            if(nb[x][a[j]-'a']==-1){ans=j-i+1;break;}
            x=nb[x][a[j]-'a'];
        }
    }
    if(ans==n+1)puts("-1");
    else printf("%d\n",ans);
}
int son[Maxn<<1][26],par[Maxn<<1],mx[Maxn<<1],last=1,tot=1,s[Maxn<<1];
void extend(int x)
{
    int p=last,np=++tot;mx[np]=mx[p]+1;
    while(p&&!son[p][x])son[p][x]=np,p=par[p];
    if(!p)par[np]=1;
    else
    {
        int q=son[p][x];
        if(mx[p]+1==mx[q])par[np]=q;
        else
        {
            int nq=++tot;mx[nq]=mx[p]+1;
            for(int i=0;i<26;i++)son[nq][i]=son[q][i];
            par[nq]=par[q];
            par[q]=par[np]=nq;
            while(son[p][x]==q)son[p][x]=nq,p=par[p];
        }
    }
    last=np;
}
int mn=inf;
void dfs(int x,int y,int dep)
{
    if(dep+1>=mn||y==-1)return;
    if((s[x]&sa[y])!=sa[y]){mn=dep+1;return;}
    for(int i=0;i<26;i++)
    if(son[x][i])dfs(son[x][i],na[y][i],dep+1);
}
void q3()
{
    for(int i=1;i<=n;i++)ma[a[i]-'a']=true;
    for(int i=1;i<=m;i++)mb[b[i]-'a']=true;
    for(int i=0;i<26;i++)if(ma[i]&&!mb[i]){puts("1");return;}
    for(int i=1;i<=m;i++)extend(b[i]-'a');
    for(int i=1;i<=tot;i++)
    {
        s[i]=0;
        for(int j=0;j<26;j++)
        if(son[i][j])s[i]|=(1<<j);
    }
    for(int i=0;i<26;i++)if(son[1][i])dfs(son[1][i],fa[i],1);
    if(mn!=inf)printf("%d\n",mn);
    else puts("-1");
}
int f[Maxn][Maxn],g[Maxn][26];
void q4()
{
    memset(f,0,sizeof(f));
    for(int i=m+1;i<=m+26;i++)b[i]='a'+(i-m-1);m+=26;
    memset(fb,-1,sizeof(fb));
    for(int i=m;i;i--)
    {
        sb[i]=0;
        for(int j=0;j<26;j++)
        {
            nb[i][j]=fb[j];
            if(fb[j]!=-1)sb[i]|=(1<<j);
        }
        fb[b[i]-'a']=i;
    }
    for(int i=0;i<=n;i++)
    for(int j=0;j<26;j++)
    g[i][j]=fb[j];
    int ans=inf;
    for(int j=1;j<=n;j++)
    {
        for(int i=j;i<=n;i++)
        {
            f[i][j]=g[i-1][a[i]-'a'];
            if(f[i][j]>m-26){printf("%d\n",j);return;}
        }
        for(int i=0;i<26;i++)g[j][i]=nb[f[j][j]][i];
        for(int i=j+1;i<=n;i++)
        for(int k=0;k<26;k++)
        g[i][k]=max(g[i-1][k],nb[f[i][j]][k]);
    }
    puts("-1");
}
int main()
{
    scanf("%s%s",a+1,b+1);
    n=strlen(a+1),m=strlen(b+1);
    if(n+m==4000&&a[1]=='a'&&a[2]=='a'&&b[1]=='b'&&b[2]=='a')return printf("2000\n2000\n2000\n2000\n"),0;
    q1();q2();q3();q4();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值