码迷,mamicode.com
首页 > 其他好文 > 详细

P3181 [HAOI2016]找相同字符

时间:2019-12-17 10:45:06      阅读:113      评论:0      收藏:0      [点我收藏+]

标签:tps   emc   while   its   tmp   int   char s   mes   --   

题意

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

如何求相同子串个数:
我们知道子串的集合即所有后缀的前缀集合,于是实际上答案就是:
\(\sum\limits_{i=1}^n\sum\limits_{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\n",ans);
    return 0;
}

P3181 [HAOI2016]找相同字符

标签:tps   emc   while   its   tmp   int   char s   mes   --   

原文地址:https://www.cnblogs.com/nofind/p/12052951.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!