zoukankan      html  css  js  c++  java
  • [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
    */
  • 相关阅读:
    error: Microsoft Visual C++ 14.0 is required.
    pip安装其他包报错
    MapReduce
    机器学习算法使用
    结巴分词使用实例
    大数据——hbase
    机房收费系统系列一:运行时错误‘-2147217843(80040e4d)’;用户‘sa’登陆失败
    耿建玲视频总结
    学生信息管理系统系列三:验收时的改进
    学生信息管理系统系列二:常见问题
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9963765.html
Copyright © 2011-2022 走看看