题目链接:点击查看
题目大意:给出两个字符串,再给出一个k,问两个字符串中长度大于等于k的公共子串有多少个(种类可重复)
题目分析:因为涉及到了子串问题,先用后缀数组跑出height数组来,接下来如果直接枚举两个字符串每一个位置的height来统计答案的话,时间复杂度是n*n,还是不能在规定时间内完成,当然这里提一嘴n*n的做法,可以先nlogn预处理出任意两个后缀的height的值,然后直接n*n枚举两个字符串的所有后缀,如果其height大于等于k,则贡献为height-k+1,显然是会超时的
那么我们可以从height数组的性质入手,因为有了k的长度限制,所以可以将height数组分为几个独立的组,保证每个组的height的最小值都是大于等于k的,现在对于其中一个满足条件的height组来说,因为任意两个位置的sa所代表的后缀的最长公共前缀,就是其区间内height的最小值,这样一来我们就可以针对字符串A统计贡献,当遇到字符串B的时候,一口气将贡献都给他,当然,维护字符串A的贡献时,我们需要根据height当前的最小值实时计算贡献,到这里我是没有想到的,看了别人的题解后才恍然大悟,原来这里用一个单调栈就可以轻松解决,单调栈在这里的作用是为了记录某一段区间上的A出现了几次的,我们需要维护的是一个大顶栈,也就是维护一个单调递增的序列,当遇到比栈顶小的数的时候,就需要将栈内比当前数大的元素一一出栈,清除其贡献并重新计算,因为计算贡献统一使用:height-k+1这个公式来计算的,之前栈内比较大的元素使用这个公式计算后,因为现在遇到了比其更小的元素了,所以显然之前计算的结果有一部分算多了,需要减去多余的这些贡献,就这样利用单调栈维护贡献即可
当然对字符串A统计完贡献后记得也对字符串B统计一遍贡献,因为在处理时先A后B和先B后A将会是两种情况
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
vector<int>ans;
char str[N];
int Stack[N],cnt[N];
int sa[N]; //SA数组,表示将S的n个后缀从小到大排序后把排好序的
//的后缀的开头位置顺次放入SA中
int t1[N],t2[N],c[N];
int rk[N],height[N],belong[N],len,k,mark;
int s[N];
bool vis[110];
void build_sa(int s[],int n,int m)//n为添加0后的总长
{
int i,j,p,*x=t1,*y=t2;
for(i=0;i<m;i++)
c[i]=0;
for(i=0;i<n;i++)
c[x[i]=s[i]]++;
for(i=1;i<m;i++)
c[i]+=c[i-1];
for(i=n-1;i>=0;i--)
sa[--c[x[i]]]=i;
for(j=1;j<=n;j<<=1)
{
p=0;
for(i=n-j;i<n;i++)
y[p++]=i;
for(i=0;i<n;i++)
if(sa[i]>=j)
y[p++]=sa[i]-j;
for(i=0;i<m;i++)
c[i]=0;
for(i=0;i<n;i++)
c[x[y[i]]]++;
for(i=1;i<m;i++)
c[i]+=c[i-1];
for(i=n-1;i>=0;i--)
sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1,x[sa[0]]=0;
for(i=1;i<n;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
if(p>=n)
break;
m=p;
}
}
void get_height(int s[],int n)//n为添加0后的总长
{
int i,j,k=0;
for(i=0;i<=n;i++)
rk[sa[i]]=i;
for(i=0;i<n;i++)
{
if(k)
k--;
j=sa[rk[i]-1];
while(s[i+k]==s[j+k])
k++;
height[rk[i]]=k;
}
}
void solve(int base=128)
{
build_sa(s,len+1,base);
get_height(s,len);
}
LL cal(bool state)
{
LL sum=0,ans=0;
int top=0;
for(int i=2;i<=len;i++)
{
if(height[i]<k)
{
sum=top=0;
}
else
{
int num=0;
if(state&&sa[i-1]<mark||!state&&sa[i-1]>mark)
{
num++;
sum+=height[i]-k+1;
}
while(top&&Stack[top]>height[i])
{
sum-=1LL*cnt[top]*(Stack[top]-k+1);
sum+=1LL*cnt[top]*(height[i]-k+1);
num+=cnt[top];
top--;
}
if(num)
{
top++;
Stack[top]=height[i];
cnt[top]=num;
}
if(state&&sa[i]>mark||!state&&sa[i]<mark)
ans+=sum;
}
}
return ans;
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
while(scanf("%d",&k)!=EOF&&k)
{
scanf("%s",str);
mark=strlen(str);
str[mark]=1;
scanf("%s",str+mark+1);
len=strlen(str);
for(int i=0;i<=len;i++)
s[i]=str[i];
solve();
printf("%lld\n",cal(true)+cal(false));
}
return 0;
}