[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\n",ans);
    return 0;
}

 

 
posted @ 2018-01-20 11:11  蒟蒻JHY  阅读(408)  评论(0编辑  收藏  举报