bzoj 4032: [HEOI2015]最短不公共子串 后缀自动机+序列自动机+bfs+记忆化搜索

来源:互联网 发布:手机闪光灯软件下载 编辑:程序博客网 时间:2024/06/07 04:44

题意

在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。
一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
A,B长度不超过2000。

分析

md一道毒瘤题,恶心的一逼。

第一个询问,我们可以枚举A子串的起点,然后在B的后缀自动机上跑即可。
第二个询问,我们仍然可以枚举A子串的起点,然后在B的序列自动机上跑即可。
第三个询问,我们可以用bfs,在A的序列自动机上跑的同时,在B的后缀自动机上跑,同时记录一个当前长度。记录一个状态vis[x,y]表示跑到后缀自动机的节点x,且当前长度为y是否被走到过。碰到能在序列自动机上跑但不能在后缀自动机上跑的情况就直接退出即可。
第四个询问,我们可以用记忆化搜索。设dp[i,j]表示走到A的序列自动机的先x,B的序列自动机的点y时,最少再往后走多少步可以得到一个A的子序列使得其不是B的子序列。直接搜即可。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=2005;int n,m,ch[N*3][26],to1[N][26],to2[N][26],ls[26],dp[N][N],fa[N*3],mx[N*3],last,cnt;char A[N],B[N];bool vis[N*3][N];queue<pair<int,pair<int,int> > > que;void ins(int x){    int p,q,np,nq;    p=last;last=np=++cnt;mx[np]=mx[p]+1;    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;    if (!p) fa[np]=1;    else    {        int q=ch[p][x];        if (mx[q]==mx[p]+1) fa[np]=q;        else        {            nq=++cnt;mx[nq]=mx[p]+1;            memcpy(ch[nq],ch[q],sizeof(ch[q]));            fa[nq]=fa[q];            fa[q]=fa[np]=nq;            for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;        }    }}void solve1(){    int ans=n+1;    for (int i=1;i<=n;i++)    {        int now=1;        for (int j=i;j<=n;j++)            if (!ch[now][A[j]-'a'])            {                ans=min(ans,j-i+1);                break;            }            else now=ch[now][A[j]-'a'];    }    if (ans==n+1) ans=-1;    printf("%d\n",ans);}void solve2(){    int ans=n+1;    for (int i=1;i<=n;i++)    {        int now=0;        for (int j=i;j<=n;j++)            if (!to2[now][A[j]-'a'])            {                ans=min(ans,j-i+1);                break;            }            else now=to2[now][A[j]-'a'];    }    if (ans==n+1) ans=-1;    printf("%d\n",ans);}void solve3(){    que.push(make_pair(0,make_pair(1,0)));    int ans=-1;    while (!que.empty())    {        pair<int,pair<int,int> > u=que.front();que.pop();        int x=u.first,y=u.second.first,len=u.second.second;        for (int i=0;i<26;i++)            if (to1[x][i]&&ch[y][i]&&!vis[ch[y][i]][len+1])            {                vis[ch[y][i]][len+1]=1;                que.push(make_pair(to1[x][i],make_pair(ch[y][i],len+1)));            }            else if (to1[x][i]&&!ch[y][i])            {                ans=len+1;                break;            }        if (ans>-1) break;    }    printf("%d\n",ans);}int dfs(int x,int y){    if (vis[x][y]) return dp[x][y];    vis[x][y]=1;    for (int i=0;i<26;i++)        if (to1[x][i]&&!to2[y][i]) return dp[x][y]=1;        else if (to1[x][i]&&to2[y][i]) dp[x][y]=min(dp[x][y],dfs(to1[x][i],to2[y][i])+1);    return dp[x][y];}void solve4(){    for (int i=0;i<=n;i++)        for (int j=0;j<=n;j++)            dp[i][j]=n+1,vis[i][j]=0;;    int ans=dfs(0,0);    if (ans==n+1) printf("%d\n",-1);    else printf("%d\n",ans);}int main(){    scanf("%s%s",A+1,B+1);    n=strlen(A+1);m=strlen(B+1);    last=cnt=1;    for (int i=1;i<=m;i++) ins(B[i]-'a');    for (int i=n;i>=0;i--)    {        for (int j=0;j<26;j++) to1[i][j]=ls[j];        if (i) ls[A[i]-'a']=i;    }    memset(ls,0,sizeof(ls));    for (int i=m;i>=0;i--)    {        for (int j=0;j<26;j++) to2[i][j]=ls[j];        if (i) ls[B[i]-'a']=i;    }    solve1();    solve2();    solve3();    solve4();    return 0;}
原创粉丝点击