题目描述
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。
输入输出格式
输入格式:
两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母
输出格式:
输出一个整数表示答案
输入输出样例
输出样例#1:
10
对于这种求两个字符串的公共子串之类的问题,一个很常见的做法是用一个不会出现的字符把它们连接起来,然后乱搞。。。
本题就是求任意一对不在同一个字符串中的后缀的lcp之和(可以画图观察hhh)。
求任意一对后缀的lcp可以利用单调栈,但是还要求不在同一数组中,所以我们再处理一个rank排名前i的在某个字符中的前缀和数量就行了。
然而还是有个大坑,在代码里注释了,,,
#include<bits/stdc++.h> #define ll long long #define maxn 400005 using namespace std; int sa[maxn],sax[maxn]; int sec[maxn],cc[maxn]; int rank[maxn<<1|1],rankx[maxn]; int n,m,height[maxn],qz[maxn][2]; int st[maxn],tp; ll ans=0; char s[maxn]; inline bool pos(int x){ return x<m; } inline void prework(){ for(int i=0;i<n;i++) cc[s[i]]++; for(int i=1;i<=500;i++) cc[i]+=cc[i-1]; for(int i=0;i<n;i++) sa[cc[s[i]]--]=i; for(int i=1;i<=n;i++){ rank[sa[i]]=i; if(i>1&&s[sa[i]]==s[sa[i-1]]) rank[sa[i]]=rank[sa[i-1]]; } int t=1; while(t<n){ memset(cc,0,sizeof(cc)); for(int i=0;i<n;i++) cc[sec[i]=rank[i+t]]++; for(int i=n-1;i>=0;i--) cc[i]+=cc[i+1]; for(int i=0;i<n;i++) sax[cc[sec[i]]--]=i; memset(cc,0,sizeof(cc)); for(int i=0;i<n;i++) cc[rank[i]]++; for(int i=1;i<=n;i++) cc[i]+=cc[i-1]; for(int i=1;i<=n;i++) sa[cc[rank[sax[i]]]--]=sax[i]; for(int i=1;i<=n;i++){ rankx[sa[i]]=i; if(i>1&&rank[sa[i]]==rank[sa[i-1]]&&sec[sa[i]]==sec[sa[i-1]]) rankx[sa[i]]=rankx[sa[i-1]]; } for(int i=0;i<n;i++) rank[i]=rankx[i]; t<<=1; } // for(int i=0;i<n;i++) printf("%d ",rank[i]); // puts(""); int now=0; for(int i=0;i<n;i++){ if(rank[i]==1){ now=0,height[1]=0; continue; } if(now) now--; int j=sa[rank[i]-1],mx=max(i,j); while(mx+now<n&&s[i+now]==s[j+now]) now++; height[rank[i]]=now; } // for(int i=1;i<=n;i++) printf("%d:%d ",pos(sa[i]),height[i]); // puts(""); //height[i]为后缀排序之后在第i个位置的后缀和i-1位置的后缀的lcp } int main(){ scanf("%s",s); m=strlen(s); s[m]='*'; scanf("%s",s+m+1); n=strlen(s); prework(); for(int i=1;i<=n;i++){ qz[i][0]=qz[i-1][0]; qz[i][1]=qz[i-1][1]; qz[i][pos(sa[i])]++; } for(int i=1;i<=n;i++){ while(tp&&height[st[tp]]>=height[i]) tp--; st[++tp]=i; int now=!pos(sa[i]); for(int j=tp;j;j--) ans+=(ll)(qz[st[j]-1][now]-qz[st[j-1]-1][now])*(ll)height[st[j]]; //因为rank[i]的后缀与rank[j](i<j)的后缀的lcp=min{height[i+1],height[i+2],,,height[j]} //所以与rank[i]的lcp等于height[st[j]]的后缀数量=(qz[st[j]-1][now]-qz[st[j-1]-1][now]) } printf("%lld ",ans); return 0; }