题意:
求两个子串的大于等于k 的重复子串有多少个。
{
求两个子串的重复子串我们可以用单调栈来做,
然而,在同一个子串中,我们想要找重复子串的个数,那就是 height数组之和。
}
思路:
两个子串,我们用后缀数组维护,把两个子串合并在一起,然后中间加一个特殊字符。
{ 看网上别人的博客。
一个n长度的子串,我们把后边填一个0,就是在位置n上加0, 这个字符串我们从 0 编号。
然后我们求后缀数组的时候,n + 1.
再求 height 的时候 n 就不用加1了。
然后,, rak 的下标 从 0 到 n-1 值是从1 到n
sa 的下标是 从 1 到 n ,,值是从0 到 n-1
height 的下标是从 2 到 n;
}
这个题。我们利用后缀数组的height数组。来计算两个子串的公共长度。
我们来看一下样例一。 用后缀数组处理完止之后,排序之后的子串。
// 尴尬,height 多写了e
我们来看看这个图片,有什么有意思的地方。
我们枚举 1 到 n,我们知道当前位置的height,然后找左右的height 的值不小于当前的height值。
在这个区间内,找找有多少子串属于第一个串,多少子串属于第二个串,然后属于两个子串的个数相乘就好了,然后把所有相乘的结果加起来。
找到的区间就是,这个区间里的所有子串都可以匹配 height 个字母。
找左右区间的时候,我们就要用到单调栈了,把L,R处理出来。
我们怎么处理在一个区间里有多少个子串属于第一个串,有多少个子串属于第二个串呢?
我们有 sa数组,它记录排名为i的子串的位置,。
我们只要开两个数组,如果第 i 个子串属于 第一个串,就 a[i] + 1, 否自, b[i] + 1, 然后前缀和,就可以知道一段区间内分别属于两个串的个数了。
这样看起来仿佛是对的, but
我们算重复了。
比如,上面的第 7 行,第 8 行,他们的height值都是2,都大于K,然后他们找到的区间又是一样的,这样就会算重复。
这时候我们新开一个vis数组,记录当前的 heigh 值,所包含的右区间到哪, 当我们再次找到这个height 值的时候,我们看看他的左区间是不是大于vis 数组,如果大于,说明这两个区间不包含,可以计算。反之就说明这两个区间是包含的,不能再计算了。
我们解决了区间包含问题,这时似乎是对的了,
但是,还是不对。
上边图片的 11 行。 他的height 值是4,包含的区间有三个子串可以匹配4个字母,
13 行,值是 3,包含的区间有 四 个子串可以匹配 三个字母,
这时候我们发现,他没有计算有多少子串可以匹配 两个字母 的。
所以我们要在开一个hh数组,记录当前位置height管理的区间,可以匹配多少种 不同的字母个数。。
比如 13 行,可以既可以匹配 3 个字母,,有可以匹配2 个字母。hh值就是2;
比如11 行,只可以匹配4 个字母的情况,hh值就是1.
这个hh值怎么算呢?
就是当前height的值,减去边界的height的值,取一个最小的就好了。
要注意一点, 边界的height的值不能小于k ,所以我们可以提前我们当前位置的 hh 值设为 height - k + 1.
之后再找最小就可以了。
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=200000+100;
char s[N],s2[N];
int sa[N],t[N],t2[N],c[N];
int height[N],rk[N];
int a[N],b[N],st[N],L[N],R[N];
int vis[N],hh[N];
// vis 记录当前高度能持续到哪里
// hh 记录当前高度和次高的相差多少。
// a 记录一段区间有多少个子串是属于a串
// b 记录一段区间有多少个子串是属于b串
// st 单调栈。
// L、R 单调栈的左右区间。
void SuffixSort(int n,int m)
{
int i,*x=t,*y=t2;
for(i=0;i=0;--i) sa[--c[x[i]]]=i;
for(int k=1;k<=n;k<<=1)
{
int p=0;
for(i=n-k;i=k) y[p++]=sa[i]-k;
for(i=0;i=0;--i) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;x[sa[0]]=0;
for(i=1;i=n) break;
m=p;
}
}
void getHeight(int n)
{
int i,j,k=0;
for(i=1;i<=n;++i) rk[sa[i]]=i;
for(i=0;i n1) b[i] = b[i-1]+1; else b[i] = b[i-1];
}
t = 1;
for(int i = 1; i <= n; i++){
while(t > 1 && height[st[t-1]] >= height[i])t--;
L[i] = (t == 1) ? 1 : (st[t-1]);
st[t++] = i;
hh[i] = min(height[i] - height[L[i]],height[i]-k+1);
}
t = 1;
for (int i = n; i >= 1; i--){
while(t > 1 && height[st[t-1]] >= height[i])t--;
R[i] = (t == 1)?n:st[t-1]-1;
st[t++] = i;
if (R[i] != n) hh[i] = min(hh[i],height[i] - height[R[i]+1]);
}
}
long long the_end(int n, int k){
memset(vis,0,sizeof(vis));
long long ans = 0;
for (int i = 1; i <= n; i++){
if (height[i] >= k && L[i] > vis[height[i]]){
vis[height[i]] = R[i];
// printf("%d %d %d %d\n",i+2,a[R[i]] - a[L[i]-1],b[R[i]] - b[L[i]-1],hh[i]);
ans += (long long)(a[R[i]] - a[L[i]-1])*(b[R[i]] - b[L[i]-1])*hh[i];
}
}
return ans;
}
int main()
{
int k;
while(~scanf("%d",&k) && k){
int n1,n;
scanf("%s",s);
n1=strlen(s);
s[n1]='$';
scanf("%s",s+n1+1);
n=strlen(s);
SuffixSort(n+1,150); //字符后面补一个0,所以n+1.
getHeight(n); //算这个的时候不用加0.
slove(n,n1,k);
long long ans = the_end(n,k);
printf("%I64d\n",ans);
}
return 0;
}