P3181 [HAOI2016]找相同字符

Posted nofind

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3181 [HAOI2016]找相同字符相关的知识,希望对你有一定的参考价值。

题意

考虑将(s1)(s2)接在一起求出相同子串个数,再求出(s1)自己匹配的相同子串个数和(s2)自己匹配的相同子串个数减去即可。

如何求相同子串个数:
我们知道子串的集合即所有后缀的前缀集合,于是实际上答案就是:
(sumlimits_{i=1}^nsumlimits_{j=i+1}^nlcp(sa_i,sa_j))

接下来就和这题相同了。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=400010;
int n,m,num,top;
int sa[maxn],rk[maxn],oldrk[maxn],id[maxn],tmpid[maxn],cnt[maxn],height[maxn],L[maxn],R[maxn],sta[maxn];
ll ans;
char s1[maxn],s2[maxn],s[maxn];
inline bool check(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
inline void get_sa(char* s,int len)
{
    memset(cnt,0,sizeof(cnt));
    num=300;
    for(int i=1;i<=len;i++)cnt[rk[i]=s[i]]++;
    for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
    for(int i=len;i;i--)sa[cnt[rk[i]]--]=i;
    for(int t=1;t<=len;t<<=1)
    {
        int tot=0;
        for(int i=len-t+1;i<=len;i++)id[++tot]=i;
        for(int i=1;i<=len;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
        tot=0;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=len;i++)cnt[tmpid[i]=rk[id[i]]]++;
        for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
        for(int i=len;i;i--)sa[cnt[tmpid[i]]--]=id[i];
        memcpy(oldrk,rk,sizeof(rk));
        for(int i=1;i<=len;i++)rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
        num=tot;
        if(num==len)break;
    }
    for(int i=1,j=0;i<=len;i++)
    {
        if(j)j--;
        while(s[i+j]==s[sa[rk[i]-1]+j])j++;
        height[rk[i]]=j;
    }
}
inline ll calc(int len)
{
    ll res=0;
    sta[top=1]=1;
    for(int i=2;i<=len;i++)
    {
        while(top&&height[sta[top]]>=height[i])R[sta[top--]]=i;
        L[i]=sta[top];sta[++top]=i;
    }
    while(top)R[sta[top--]]=len+1;
    for(int i=2;i<=len;i++)res+=1ll*height[i]*(R[i]-i)*(i-L[i]);
    return res;
}
int main()
{
    scanf("%s%s",s1+1,s2+1);
    n=strlen(s1+1),m=strlen(s2+1);
    get_sa(s1,n);ans-=calc(n);
    get_sa(s2,m);ans-=calc(m);
    for(int i=1;i<=n;i++)s[i]=s1[i];
    s[n+1]='#';
    for(int i=1;i<=m;i++)s[n+i+1]=s2[i];
    get_sa(s,n+m+1);ans+=calc(n+m+1);
    printf("%lld
",ans);
    return 0;
}

以上是关于P3181 [HAOI2016]找相同字符的主要内容,如果未能解决你的问题,请参考以下文章

P3181 [HAOI2016]找相同字符

luogu P3181 [HAOI2016]找相同字符

bzoj4566: [Haoi2016]找相同字符

bzoj4566HAOI2016找相同字符

BZOJ4566:[Haoi2016]找相同字符

bzoj 4566 [Haoi2016]找相同字符SA