后缀数组+栈的线性扫描统计两个字符串的长度不少于K的公共子串个数
separate()构造原字符串str1和str2的高度数组,根据lcp(sa[i],sa[j])=rmq(height[i+1..j]),注意到各后缀在合并后得到的字符串中保持在原字符串中的字典序
#include
<
iostream
>
using
namespace
std;
#define
MAXN 200010
#define
clr(x) memset(x,0,sizeof(x))
int
b[MAXN],array[
4
][MAXN],
*
sa,
*
nsa,
*
rank,
*
nrank,
h1[MAXN],h2[MAXN],h3[MAXN],K,n1,n2,n3;
char
str1[MAXN
/
2
],str2[MAXN
/
2
],str3[MAXN];
int
st[MAXN];
void
make_sa(){
int
i,j,k;
sa
=
array[
0
];
nsa
=
array[
1
];
rank
=
array[
2
];
nrank
=
array[
3
];
memset(b,
0
,
sizeof
(b));
for
(i
=
0
;i
<
n3;i
++
)
b[str3[i]]
++
;
for
(i
=
1
;i
<=
256
;i
++
)
b[i]
+=
b[i
-
1
];
for
(i
=
n3
-
1
;i
>=
0
;i
--
)
sa[
--
b[str3[i]]]
=
i;
for
(rank[sa[
0
]]
=
0
,i
=
1
;i
<
n3;i
++
){
rank[sa[i]]
=
rank[sa[i
-
1
]];
if
(str3[sa[i]]
!=
str3[sa[i
-
1
]])
rank[sa[i]]
++
;
}
for
(k
=
1
;k
<
n3
&&
rank[sa[n3
-
1
]]
<
n3
-
1
;k
*=
2
){
for
(i
=
0
;i
<
n3;i
++
)
b[rank[sa[i]]]
=
i;
for
(i
=
n3
-
1
;i
>=
0
;i
--
)
if
(sa[i]
-
k
>=
0
)
nsa[b[rank[sa[i]
-
k]]
--
]
=
sa[i]
-
k;
for
(i
=
n3
-
k;i
<
n3;i
++
)
nsa[b[rank[i]]
--
]
=
i;
for
(nrank[nsa[
0
]]
=
0
,i
=
1
;i
<
n3;i
++
){
nrank[nsa[i]]
=
nrank[nsa[i
-
1
]];
if
(rank[nsa[i]]
!=
rank[nsa[i
-
1
]]
||
rank[nsa[i]
+
k]
!=
rank[nsa[i
-
1
]
+
k])
nrank[nsa[i]]
++
;
}
int
*
t
=
sa;sa
=
nsa;nsa
=
t;
t
=
rank;rank
=
nrank;nrank
=
t;
}
for
(i
=
0
,k
=
0
;i
<
n3;i
++
){
if
(rank[i]
==
0
)
h3[rank[i]]
=
0
;
else
{
for
(j
=
sa[rank[i]
-
1
];str3[i
+
k]
==
str3[j
+
k];k
++
);
h3[rank[i]]
=
k;
if
(k
>
0
)
k
--
;
}
}
}
__int64 solve(
int
h[],
int
n){
int
i
=
0
,t,top
=
0
,m,num;
__int64 cnt
=
0
;
st[top]
=
0
;
h[
0
]
=
h[n]
=
K
-
1
;
while
(i
<=
n){
t
=
h[st[top]];
//
定义与height[st[top]]相关的后缀为栈顶后缀(乱编的)
if
(h[i]
<
K
&&
top
==
0
)
i
++
;
else
if
(h[i]
>
t)
st[
++
top]
=
i
++
;
else
if
(h[i]
==
t)
i
++
;
else
{
m
=
i
-
st[top]
+
1
;
if
(h[i]
>=
K
&&
h[i]
>
h[st[top
-
1
]]){
//
统计与当前后缀和栈顶后缀的公共前缀不相关的
//
且属于栈顶后缀的公共前缀的部分(再读一遍?)
num
=
t
-
h[i];
h[st[top]]
=
h[i];
//
下次统计当前后缀和栈顶后缀的长度为h[i]的公共前缀部分
}
else
//
当前后缀不能与栈顶后缀构成长度至少为K公共前缀,故暂不
//
纳入统计,且看它与下一个后缀能否构成公共K前缀
num
=
t
-
h[st[
--
top]];
cnt
+=
(__int64)m
*
(m
-
1
)
/
2
*
num;
}
}
return
cnt;
}
void
separate(){
clr(h1);
clr(h2);
int
mn1
=
INT_MAX,mn2
=
INT_MAX,i,cnt1
=
0
,cnt2
=
0
;
for
(i
=
0
;i
<
n3;i
++
){
if
(h3[i]
<
mn1)
mn1
=
h3[i];
if
(h3[i]
<
mn2)
mn2
=
h3[i];
if
(sa[i]
<
n1){
h1[cnt1
++
]
=
mn1;
mn1
=
INT_MAX;
}
else
{
h2[cnt2
++
]
=
mn2;
mn2
=
INT_MAX;
}
}
}
int
main(){
__int64 ans;
while
(scanf(
"
%d
"
,
&
K)
&&
K){
scanf(
"
%s%s
"
,str1,str2);
n1
=
strlen(str1);
n2
=
strlen(str2);
str1[n1
++
]
=
'
#
'
;
str1[n1]
=
'
\0
'
;
str2[n2
++
]
=
'
$
'
;
str2[n2]
=
'
\0
'
;
strcpy(str3,str1);
strcat(str3,str2);
n3
=
n1
+
n2;
make_sa();
separate();
ans
=
solve(h3,n3)
-
solve(h2,n2)
-
solve(h1,n1);
printf(
"
%I64d\n
"
,ans);
}
return
0
;
}