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

    Description

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

    Solution

    题中所求即为A串与B串任意一组height之和,所以暴力可以做到$O(n^2)$

    考虑将答案挂在这两组后缀中sa值较大的那个上,即每个后缀只考虑出现在它前的所有后缀贡献的答案

    由于lcp长度为在这两排名间height的最小值,可以按排名枚举后缀,单调栈维护答案

    可以做到$O(n log n)$或$O(n)$

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<stack>
    using namespace std;
    int n,m=127,len1,len2,buc[400010],x[400010],y[400010],sa[400010],rk[400010],height[400010],sum[400010];
    long long ans;
    char s[400010],s2[200005];
    struct Node
    {
        int id;
        long long val;
    };
    stack<Node>sta;
    inline int read()
    {
        int w=0,f=1;
        char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
        return w*f;
    }
    void getsa()
    {
        for(int i=1;i<=n;i++) ++buc[x[i]=s[i]];
        for(int i=2;i<=m;i++) buc[i]+=buc[i-1];
        for(int i=n;i;i--) sa[buc[x[i]]--]=i;
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;i++) y[++num]=i;
            for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
            for(int i=1;i<=m;i++) buc[i]=0;
            for(int i=1;i<=n;i++) ++buc[x[i]];
            for(int i=2;i<=m;i++) buc[i]+=buc[i-1];
            for(int i=n;i;i--) sa[buc[x[y[i]]]--]=y[i],y[i]=0;
            swap(x,y),x[sa[1]]=1,num=1;
            for(int 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 getheight()
    {
        int k=0;
        for(int i=1;i<=n;i++) rk[sa[i]]=i;
        for(int i=1;i<=n;i++)
        {
            if(rk[i]==1) continue;
            if(k) --k;
            int j=sa[rk[i]-1];
            while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;
            height[rk[i]]=k;
        }
    }
    int main()
    {
        scanf("%s",s+1),len1=strlen(s+1),s[len1+1]='z'+1,scanf("%s",s2+1),len2=strlen(s2+1);
        for(int i=1;i<=len2;i++) s[len1+1+i]=s2[i];
        n=strlen(s+1),getsa(),getheight(),sta.push((Node){1,0});
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(sa[i]<=len1);
        for(int i=2;i<=n;i++)
        {
            while(sta.size()&&height[sta.top().id]>height[i]) sta.pop();
            Node temp=sta.top();
            sta.push((Node){i,(sum[i-1]-sum[temp.id-1])*height[i]+temp.val});
            if(sa[i]>len1+1) ans+=sta.top().val;
        }
        while(sta.size()) sta.pop();
        sta.push((Node){1,0});
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(sa[i]>len1+1);
        for(int i=2;i<=n;i++)
        {
            while(sta.size()&&height[sta.top().id]>height[i]) sta.pop();
            Node temp=sta.top();
            sta.push((Node){i,(sum[i-1]-sum[temp.id-1])*height[i]+temp.val});
            if(sa[i]<=len1) ans+=sta.top().val;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    [HAOI2016]找相同字符
  • 相关阅读:
    安装Php时候报错信息:virtual memory exhausted: Cannot allocate memory (不能分配内存)
    putty保持连接不自动段开
    利用iptables将本地的80端口请求转发到8080,当前主机ip为192.168.1.1,命令怎么写?
    linux上大量tcp端口处于TIME_WAIT的问题
    cacti出现snmp error
    洛谷3672:小清新签到题——题解
    BZOJ3040:最短路——题解
    洛谷4230:连环病原体——题解
    洛谷3934:Nephren Ruq Insania——题解
    洛谷3932:浮游大陆的68号岛——题解
  • 原文地址:https://www.cnblogs.com/JDFZ-ZZ/p/14220407.html
Copyright © 2011-2022 走看看