• bzoj 4032(A的一个最短的子串,它不是B的子串 || A的一个最短的子串,它不是B的子序列 || A的一个最短的子序列,它不是B的子串||A的一个最短的子序列,它不是B的子序列)


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

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

    Input

    有两行,每行一个小写字母组成的字符串,分别代表A和B。

    Output

    输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.

    Sample Input

    aabbcc abcabc

    Sample Output

    2 4 2 4

    HINT

    #include<iostream>  
    #include<cstring>  
    #include<algorithm>  
    #include<cmath>  
    #include<cstdio>
    #include<map>  
    #define N 20013  
    #define p1 2000001001
    #define ull unsigned long long
    using namespace std;  
    int cnt,n,m,np,p,q,nq,l[N],r[N],pos[N],root;  
    int ch[N][30],fa[N],len[N],last,ch1[N][30],f[2003][2003];  
    ull mi[N];
    char s[N],s1[N];  
    map<int,int> mp;
    map<ull,int> mp1; 
    void extend(int i)  
    {  
        int c=s1[i]-'a'+1;  
        p=last; np=last=++cnt; l[np]=l[p]+1;  
        for (;!ch[p][c]&&p;p=fa[p]) ch[p][c]=np;  
        if (!p) fa[np]=root;  
        else {  
            q=ch[p][c];  
            if (l[q]==l[p]+1) fa[np]=q;  
            else {  
                nq=++cnt; l[nq]=l[p]+1;  
                memcpy(ch[nq],ch[q],sizeof(ch[q]));  
                fa[nq]=fa[q];  
                fa[q]=fa[np]=nq;  
                for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;  
            }  
        }   
    }  
    void solve1()  
    {  
        /*mi[0]=1;
        for (int i=1;i<=max(n,m)+2;i++)  mi[i]=mi[i-1]*p1;
        for (int i=1;i<=m;i++){
            ull x=(ull)s1[i]*mi[1]; mp1[x]=1;
            //cout<<x<<endl;
            for (int j=i+1;j<=m;j++) {
                x=x*p1+s1[j]*mi[i]; 
                mp1[x]=(j-i+1);
            //    cout<<x<<endl;
            }
        } 
        int mn=m+1;
        for (int i=1;i<=n;i++) {
            ull x=(ull)s[i]*mi[1];
            if (!mp1[x]) mn=min(mn,1);
            for (int j=i+1;j<=n;j++){
                x=x*p1+s[j]*mi[1];
                if (!mp1[x]&&j-i+1<=n) mn=min(mn,j-i+1);
            } 
        }
        if (mn>=m+1) PRintf("-1
    ");
        else printf("%d
    ",mn);*/
        memset(f,0,sizeof(f));
        int mn=m+1;
        for (int i=1;i<=n;i++){
            int a=0,b=0;
            for (int j=1;j<=n;j++) {
                if (s[i]==s1[j]) f[i][j]=f[i-1][j-1]+1;
                a=max(f[i][j],a);
            }
            if (a!=i) mn=min(mn,a);
        }
        if (mn>=m+1) printf("-1
    ");
        else printf("%d
    ",mn+1);
    } 
    void solve2()//A的最短子串,不是B的子序列 
    {
        memset(f,0,sizeof(f));
        int mn=m+1;
        for (int i=1;i<=n;i++){
            int a=0,b=0;
            for (int j=1;j<=m;j++) {
                if (s[i]==s1[j]) f[i][j]=b+1;
                a=max(a,f[i][j]);
                b=max(b,f[i-1][j]); //f[i][j]表示A中的第i个字符匹配到B中第j个字符,A匹配时必须连续 
            } 
            if (a!=i) mn=min(mn,a);//匹配的长度不是i说明,在i+前一字符就是一个合法的子串 
        }
        if (mn>=m+1) printf("-1
    ");
        else printf("%d
    ",mn+1);
    } 
    void solve3()//A的最短的子序列,不是b的子串。 
    {
        memset(len,127/3,sizeof(len));
        len[1]=0; int mn=m+1; int t;
        for (int i=1;i<=n;i++)
         for (int j=1;j<=cnt;j++)
          if (!(t=ch[j][s[i]-'a'+1])) mn=min(mn,len[j]);//如果到该点匹配不上了,说明在当前基础上加入s[i]就能得到了一个合法的子序列 
          else len[t]=min(len[t],len[j]+1);//len表示的是用a的子序列去匹配后缀自动机中的节点,到节点i能得到的最短长度 
        if (mn==m+1) printf("-1
    ");
        else printf("%d
    ",mn+1);
    }
    void solve4()
    {
        for (int i=m;i>=0;i--){//只要是在当前位置之后出现的字符都可以用来构造子序列,如果我们要选择字符x,那么必然优先选择最靠近当前位置的字符。 
            for (int j=1;j<=26;j++)
             if (mp[j]) ch1[i][j]=mp[j];
            mp[s1[i]-'a'+1]=i;
        }
        memset(len,127/3,sizeof(len));
        len[0]=0;
        int mn=m+1; int t;
        for (int i=1;i<=n;i++)
         for (int j=m;j>=0;j--)//这里与第三问不同,必须倒序枚举,因为这里相当于是个背包,那么对于一个字符来说,不能连续更新同一层中的位置,否则原串只有一个字符x,正序枚举会出现xx的情况。 
          if (!(t=ch1[j][s[i]-'a'+1])) mn=min(mn,len[j]);
          else len[t]=min(len[t],len[j]+1); 
        if (mn==m+1) printf("-1
    ");
        else printf("%d
    ",mn+1);
    }
    int main()  
    {  
        freopen("sus4.in","r",stdin);  
        //freopen("sus.out","w",stdout);  
        scanf("%s",s+1);  
        scanf("%s",s1+1);  
        n=strlen(s+1); m=strlen(s1+1);   
        root=last=++cnt;  
        for (int i=1;i<=m;i++)  
         extend(i);  
        solve1();  
        solve2();
        solve3();
        solve4();
    }  
    View Code

    先留坑 ,待我变的更厉害好吧

  • 相关阅读:
    Xshell如何配置并远程连接Linux服务器详解
    VMware如何克隆一个虚拟机
    如何通过VMware安装Linux CentOS 7.7系统
    如何VMware创建Linux虚拟机并设置虚拟机网络
    自动化运维工具Ansible之LNMP实践环境部署
    自动化运维工具Ansible之Roles角色详解
    自动化运维工具Ansible之Tests测验详解
    Ansible Jinja2 模板使用
    Ansible playbook Vault 加密
    Ansible playbook 编程
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/10693185.html
Copyright © 2020-2023  润新知