【问题描述】 | |||
给出两个字符串T和P,请你用后缀数组完成下列任务: 任务1、计算这两个字符串的最长公共子串,并输出字典序最小的公共串。 任务2、计算长度不小于K的公共子串的个数(子串相同但位置不同算不同的子串)。 例如:T="xx",P="xx",K=1,则长度不小于K 的公共子串的个数是5。 再如:T="aababaa",P="abaabaa",K=2,则长度不小于K 的公共子串的个数是22。 |
【输入】 | |||
含3行,第一行是字符串T,第二行是字符串P,第3行一个整数K。 |
【输出】 | |||
含3行: 第1行一个整数,表示T和P的最长公共子串长度。 第2行一个字符串,表示T和P字典序最小的公共子串。 第3行一个字符串,表示T和P长度不小于K的公共子串数目。 分别对应任务1和任务2的答案。 |
【样例输入】 | |||
aababaa abaabaa 2 |
【样例输出】 | |||
4 aaba 22 |
【数据范围】 | |||
字符串长度不超过100 000,1<=K<=100 000 |
|
|
| |
| 【样例解释】 |
| |
|
|
【问题分析】
task1:
可以等价于求两串所有后缀的最长公共前缀。
加一个奇葩的分隔符将两串连接起来,并标记新串s中的每个字符s[i](s[i]属于T则将i标记为1,s[i]属于P则将i标记为2,s[i]为分隔符则标记为0),求后缀数组,求height数组。
显然,如果求一个串的最长重复子串(可重叠),则ans=max(height[i]),但本题要求不是同一个字符串的两个后缀,所以更新答案前先判断一下sa[i]与sa[i-1]是否分属T,P即可。
task2:
对于P,T的两个后缀i,j,若LCP(i,j)=l,则对答案的贡献为l-k+1。
按task1连接成新串后,按height分组,每组内相邻后缀的height>=K,显然不同组之间是相互独立的,对于每一组,要求出所有P之前的T与之LCP-K+1的和,以及所有T之前的P与之的LCP-K+1的和,累加到ans中,显然暴力枚举的时间复杂度为O(N^2)。
经分析,这又是两个等价问题。现在只考虑求出所有P在同一组内之前的所有T与之LCP-K+1的和。
对于任意一个P来说,在它前面的T与之的LCP是单调不减的。从左到右扫描sa,当前元素为i,设h=height[i+1]-K+1为当前i与P0(i右边同一组元素中最近的P)的答案贡献。设一个单调递增的栈stack,stack[top].h为栈顶元素与当前P0的答案贡献,stack[top].x为答案贡献为stack[top].h的T元素有多少个。设sum=本组内当前所有扫过的T与P0的总贡献。
若当前元素i为P,ans=ans+sum;
将当前元素加入栈中,维护sum;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXN 100010 using namespace std; typedef long long LL; //task1:加一个奇葩的分隔符将两串连接起来。分属两串 的height中最大值即为所求 //task2:对每个P(T)统计前面的T(P)的LCP-K+1之和。扫两遍height //快速统计:维护height的单调递增 栈(对P统计T为例) //sum为当前组内所有’T‘对当前元素的LCP-K+1之和 //当前元素为'P',累加ans //将当前元素的height及id加入单调栈,维护sum char T[MAXN],P[MAXN],s[MAXN<<1]; int K,n; int old[MAXN<<1]; int sa[MAXN<<1],tsa[MAXN<<1],c[MAXN<<1],x[MAXN<<1],y[MAXN<<1],z[MAXN<<1]; int rank[MAXN<<1],height[MAXN<<1]; void getsa(char *s,int n,int m) { int i,p; for(i=0;i
=0;i--) sa[--c[s[i]]]=i; for(int k=1;k<=n;k<<=1){ for(i=0;i
=0;i--) sa[--c[x[tsa[i]]]]=tsa[i]; z[sa[0]]=p=1; for(i=1;i
>T>>P>>K; int i; n=0; for(i=0;T[i];i++){ old[n]=1; s[n++]=T[i]; } old[n]=0; s[n++]=125; for(i=0;P[i];i++){ old[n]=2; s[n++]=P[i]; } getsa(s,n,150); getheight(s,n); } LL ans; char maxpub[MAXN<<1],t[MAXN<<1]; void task1() { ans=0; for(int i=1;i<=n;i++){ int p=sa[i],q=sa[i-1]; if(old[p]!=old[q]){ if(ans
=h){ sum+=((LL)h-stack[top].h)*stack[top].x; x+=stack[top].x; top--; } if(old[p]==A) sum+=h; stack[++top]=(Stack){h,x}; } } void task2() { ans=0; deal(1,2); deal(2,1); cout<
<