• [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;
    }
     
  • 相关阅读:
    题解 AT5228 【[ABC162A] Lucky 7】
    题解 P6467 【[COCI2008-2009#6] BUKA】
    2020 Codeforces 愚人节比赛题解 A~D
    题解 AT4251 【[ABC110A] Maximize the Formula】
    题解 AT5638 【November 30】
    题解 AT4164 【[ABC102A] Multiple of 2 and N】
    多项式全家桶
    烂题推荐
    NOIP 2020 游记
    P5048 题解
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8320132.html
Copyright © 2020-2023  润新知