zoukankan      html  css  js  c++  java
  • [HAOI2016]找相同字符

    题目描述

    给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。

    输入输出格式

    输入格式:

    两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

    输出格式:

    输出一个整数表示答案

    输入输出样例

    输入样例#1: 
    aabb
    bbaa
    输出样例#1: 
    10


    对于这种求两个字符串的公共子串之类的问题,一个很常见的做法是用一个不会出现的字符把它们连接起来,然后乱搞。。。
    本题就是求任意一对不在同一个字符串中的后缀的lcp之和(可以画图观察hhh)。
    求任意一对后缀的lcp可以利用单调栈,但是还要求不在同一数组中,所以我们再处理一个rank排名前i的在某个字符中的前缀和数量就行了。
    然而还是有个大坑,在代码里注释了,,,
    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 400005
    using namespace std;
    int sa[maxn],sax[maxn];
    int sec[maxn],cc[maxn];
    int rank[maxn<<1|1],rankx[maxn];
    int n,m,height[maxn],qz[maxn][2];
    int st[maxn],tp;
    ll ans=0;
    char s[maxn];
    
    inline bool pos(int x){
        return x<m;
    } 
    
    inline void prework(){
        for(int i=0;i<n;i++) cc[s[i]]++;
        for(int i=1;i<=500;i++) cc[i]+=cc[i-1];
        for(int i=0;i<n;i++) sa[cc[s[i]]--]=i;
        for(int i=1;i<=n;i++){
            rank[sa[i]]=i;
            if(i>1&&s[sa[i]]==s[sa[i-1]]) rank[sa[i]]=rank[sa[i-1]];
        }
        
        int t=1;
        while(t<n){
            memset(cc,0,sizeof(cc));
            for(int i=0;i<n;i++) cc[sec[i]=rank[i+t]]++;
            for(int i=n-1;i>=0;i--) cc[i]+=cc[i+1];
            for(int i=0;i<n;i++) sax[cc[sec[i]]--]=i;
            
            memset(cc,0,sizeof(cc));
            for(int i=0;i<n;i++) cc[rank[i]]++;
            for(int i=1;i<=n;i++) cc[i]+=cc[i-1];
            for(int i=1;i<=n;i++) sa[cc[rank[sax[i]]]--]=sax[i];
            
            for(int i=1;i<=n;i++){
                rankx[sa[i]]=i;
                if(i>1&&rank[sa[i]]==rank[sa[i-1]]&&sec[sa[i]]==sec[sa[i-1]]) rankx[sa[i]]=rankx[sa[i-1]];
            } 
            
            for(int i=0;i<n;i++) rank[i]=rankx[i];
            t<<=1;
        }
        
    //    for(int i=0;i<n;i++) printf("%d ",rank[i]);
    //    puts("");
        
        int now=0;
        for(int i=0;i<n;i++){
            if(rank[i]==1){
                now=0,height[1]=0;
                continue;
            }
            if(now) now--;
            int j=sa[rank[i]-1],mx=max(i,j);
            while(mx+now<n&&s[i+now]==s[j+now]) now++;
            
            height[rank[i]]=now;
        }
        
    //    for(int i=1;i<=n;i++) printf("%d:%d ",pos(sa[i]),height[i]);
    //    puts("");
        
        //height[i]为后缀排序之后在第i个位置的后缀和i-1位置的后缀的lcp 
    }
    
    int main(){
        scanf("%s",s);
        m=strlen(s);
        s[m]='*';
        scanf("%s",s+m+1);
        n=strlen(s);
        
        prework();
        
        for(int i=1;i<=n;i++){
            qz[i][0]=qz[i-1][0];
            qz[i][1]=qz[i-1][1];
            qz[i][pos(sa[i])]++;
        }
        
        for(int i=1;i<=n;i++){
            while(tp&&height[st[tp]]>=height[i]) tp--;
            st[++tp]=i;
            int now=!pos(sa[i]);
            for(int j=tp;j;j--) ans+=(ll)(qz[st[j]-1][now]-qz[st[j-1]-1][now])*(ll)height[st[j]];
            
            //因为rank[i]的后缀与rank[j](i<j)的后缀的lcp=min{height[i+1],height[i+2],,,height[j]}
            //所以与rank[i]的lcp等于height[st[j]]的后缀数量=(qz[st[j]-1][now]-qz[st[j-1]-1][now]) 
        }
        
        printf("%lld
    ",ans);
        return 0;
    }
     
  • 相关阅读:
    V-Box
    One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:
    获取用户地理位置
    错误 CS0234 命名空间“Microsoft”中不存在类型或命名空间名“Reporting”(是否缺少程序集引用?)
    “/Reports”应用程序中的服务器错误。
    微信公众号关注界面的菜单栏跟实际的菜单栏不一致
    错误 CS8107 C# 7.0 中不支持功能“xxxxxx”。请使用 7.1 或更高的语言版本。
    已经安装了 AccessDatabaseEngine.exe,还是报错
    没有足够的内存继续执行程序(mscorlib)
    坑,坑,
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8320132.html
Copyright © 2011-2022 走看看