[HAOI2016]找相同字符

题目描述

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

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

 

题解:

s1'#'s2拼起来。

求height

要求∑(i)∑(j)LCP(rk[i],rk[j]) 1<=i<=l1, l1+2<=j<=l1+l2+1

考虑找排名序列。

其实是对属于s1的i之前的,属于s2的j,min(hei[k]),j+1<=k<=i

对一些前面的某些位置取min再做和

 

可以分治。

对于跨中点的区间,钦定最小值在左边,做一遍

钦定最小值在右边,做一遍

对于两边最小值相同的,一个取等一个不取等

height[i]=lcp(suff[sa[i],suff[sa[i-1]])

注意边界处理

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
int l1,l2;
char s1[N],s2[N],s[2*N];
int x[4*N],y[4*N],rk[2*N],sa[2*N];
int c[2333],num;
int hei[2*N];
void getsa(){
    m=233;
    for(reg i=1;i<=n;++i) ++c[x[i]=s[i]];
    //for(reg i=1;i<=n;++i) cout<<x[i]<<" ";cout<<endl;
    for(reg i=2;i<=m;++i) c[i]+=c[i-1];
    for(reg i=1;i<=n;++i) sa[c[x[i]]--]=i;
    //for(reg i=1;i<=n;++i) cout<<sa[i]<<" ";cout<<endl;
    for(reg k=1;k<=n;k<<=1){
        num=0;
        for(reg i=n-k+1;i<=n;++i) y[++num]=i;
        for(reg i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;
        for(reg i=1;i<=m;++i) c[i]=0;
        for(reg i=1;i<=n;++i) ++c[x[i]];
        for(reg i=2;i<=m;++i) c[i]+=c[i-1];
        for(reg i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        num=1;x[sa[1]]=1;
        for(reg i=2;i<=n;++i){
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
        }
        if(num==n) break;
        m=num;
        
    } 
}
void gethei(){
    int k=0;
    for(reg i=1;i<=n;++i) rk[sa[i]]=i;
    for(reg i=1;i<=n;++i){
        if(rk[i]==1) continue;
        if(k) --k;
        int j=sa[rk[i]-1];
        while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;
        hei[rk[i]]=k;
    }
}
ll ans;
int be[2*N];
void divi(int l,int r){
//    cout<<" divi "<<l<<" and "<<r<<"-------------------------------"<<endl;
    if(l==r) return ;
    int mid=(l+r)>>1;
//    cout<<" mid "<<mid<<endl;
    int cnt1=0,cnt2=0;
    int mir=inf,mil=inf;
    int ptr=mid+1;
    for(reg i=mid+1;i<=r;++i){
        mir=min(mir,hei[i]);
        while(ptr-1>=l&&hei[ptr]>=mir) {
            if(be[ptr-1]==1) ++cnt1;
            else if(be[ptr-1]==2) ++cnt2;
            ptr--;
        }
        if(be[i]==1) ans+=(ll)cnt2*mir;
        else if(be[i]==2) ans+=(ll)cnt1*mir;
    }
    cnt1=0,cnt2=0;
    ptr=mid+1;
    for(reg i=mid;i>=l;--i){
        mil=min(mil,hei[i+1]);
        while(ptr<=r&&hei[ptr]>mil){
            if(be[ptr]==1) ++cnt1;
            else if(be[ptr]==2) ++cnt2;
            ptr++;
        }
        if(be[i]==1) ans+=(ll)cnt2*mil;
        else if(be[i]==2) ans+=(ll)cnt1*mil;
    }
    divi(l,mid);
    divi(mid+1,r);
}
int main(){
    scanf("%s%s",s1+1,s2+1);
    l1=strlen(s1+1);l2=strlen(s2+1);
    n=l1+l2+1;
    for(reg i=1;i<=l1;++i) s[i]=s1[i];
    s[l1+1]='#';
    for(reg i=1;i<=l2;++i) s[l1+1+i]=s2[i];
    
//    cout<<n<<" : "<<s+1<<endl;
    
    getsa();gethei();
    
    for(reg i=1;i<=l1;++i) be[rk[i]]=1;
    be[rk[l1+1]]=3;
    for(reg i=1;i<=l2;++i) be[rk[l1+1+i]]=2;
    
//    for(reg i=1;i<=n;++i){
//        cout<<hei[i]<<" ";
//    }cout<<endl;
//    for(reg i=1;i<=n;++i){
//        cout<<be[i]<<" ";
//    }cout<<endl;
//    
    
    divi(1,n);
    printf("%lld",ans);
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/15 10:51:22
*/

 

 

更优秀地,可以用单调栈做;

 

 从顶到底,高度单调递减,高度即位置与当前i之间的height取min

维护这个矩形集合的面积。

遇到hei[i]<hei[top]的情况,直接砍下去,然后必要的时候,和sta[top-1]合并

stack中每个元素维护w和h,表示矩形宽和高。

全局数组now维护面积

 

s1,s2在前分别做一遍

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=200000+5;
const int inf=0x3f3f3f3f;
int n,m;
int l1,l2;
char s1[N],s2[N],s[2*N];
int x[4*N],y[4*N],rk[2*N],sa[2*N];
int c[2333],num;
int hei[2*N];
struct po{
    int w,h;
    po(){};
    po(int a,int b){
        w=a,h=b;
    }
}sta[2*N];
int top;
void getsa(){
    m=233;
    for(reg i=1;i<=n;++i) ++c[x[i]=s[i]];
    //for(reg i=1;i<=n;++i) cout<<x[i]<<" ";cout<<endl;
    for(reg i=2;i<=m;++i) c[i]+=c[i-1];
    for(reg i=1;i<=n;++i) sa[c[x[i]]--]=i;
    //for(reg i=1;i<=n;++i) cout<<sa[i]<<" ";cout<<endl;
    for(reg k=1;k<=n;k<<=1){
        num=0;
        for(reg i=n-k+1;i<=n;++i) y[++num]=i;
        for(reg i=1;i<=n;++i) if(sa[i]>k) y[++num]=sa[i]-k;
        for(reg i=1;i<=m;++i) c[i]=0;
        for(reg i=1;i<=n;++i) ++c[x[i]];
        for(reg i=2;i<=m;++i) c[i]+=c[i-1];
        for(reg i=n;i>=1;--i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        num=1;x[sa[1]]=1;
        for(reg i=2;i<=n;++i){
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
        }
        if(num==n) break;
        m=num;
        
    } 
}
void gethei(){
    int k=0;
    for(reg i=1;i<=n;++i) rk[sa[i]]=i;
    for(reg i=1;i<=n;++i){
        if(rk[i]==1) continue;
        if(k) --k;
        int j=sa[rk[i]-1];
        while(j+k<=n&&i+k<=n&&s[i+k]==s[j+k]) ++k;
        hei[rk[i]]=k;
    }
}
ll ans;
ll now;
int be[2*N];
int sum1[2*N],sum2[2*N];
int main(){
    scanf("%s%s",s1+1,s2+1);
    l1=strlen(s1+1);l2=strlen(s2+1);
    n=l1+l2+1;
    for(reg i=1;i<=l1;++i) s[i]=s1[i];
    s[l1+1]='#';
    for(reg i=1;i<=l2;++i) s[l1+1+i]=s2[i];
    
//    cout<<n<<" : "<<s+1<<endl;
    
    getsa();gethei();
    
    for(reg i=1;i<=l1;++i) be[rk[i]]=1;
    be[rk[l1+1]]=3;
    for(reg i=1;i<=l2;++i) be[rk[l1+1+i]]=2;
    for(reg i=2;i<=n;++i){
        //cout<<" iiiiii "<<i<<" ------------------------ "<<endl;
        while(top&&hei[i]<sta[top].h){
            if(top>1){
                if(sta[top-1].h>hei[i]){
                    now-=(sta[top].h-sta[top-1].h)*sta[top].w;
                    sta[top-1].w+=sta[top].w;
                    --top;
                }
                else{
                    now-=(sta[top].h-hei[i])*sta[top].w;
                    sta[top].h=hei[i];
                }
            }
            else{
                now-=(sta[top].h-hei[i])*sta[top].w;
                sta[top].h=hei[i];
            }
        }
        sta[++top]=po(be[i-1]==1,hei[i]);
        now+=(be[i-1]==1)*hei[i];
        if(be[i]==2) ans+=now;
        //cout<<" ans "<<ans<<" now "<<now<<endl;
    }
    top=0;
    now=0;
    for(reg i=2;i<=n;++i){
        while(top&&hei[i]<sta[top].h){
            if(top>1){
                if(sta[top-1].h>hei[i]){
                    now-=(sta[top].h-sta[top-1].h)*sta[top].w;
                    sta[top-1].w+=sta[top].w;
                    --top;
                }
                else{
                    now-=(sta[top].h-hei[i])*sta[top].w;
                    sta[top].h=hei[i];
                }
            }
            else{
                now-=(sta[top].h-hei[i])*sta[top].w;
                sta[top].h=hei[i];
            }
        }
        sta[++top]=po(be[i-1]==2,hei[i]);
        now+=(be[i-1]==2)*hei[i];
        if(be[i]==1) ans+=now;
    }
    printf("%lld",ans);
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/15 10:51:22
*/

 

posted @ 2018-11-15 15:25  *Miracle*  阅读(289)  评论(0编辑  收藏  举报