题意
有m个长度为n的串,问其中公共的子串一共有多少个。
题解
这题第一想法是用后缀数组lcp暴力计数,但这样不是爆内存就是爆时间……仔细观察可以发现,每个序列都是一个 1 ∼ n 1\sim n 1∼n的排列,我们可以以第一个串为样式,将第一个串中的所有子串枚举出来,然后依次判断,这里枚举需要一个技巧。
假设第一个串的 s [ i + 1 , i + 2 ] s[i+1,i+2] s[i+1,i+2]在每个串中都出现,那么如果 s [ i , i + 1 ] s[i,i+1] s[i,i+1]在每个串中都出现的话, s [ i , i + 2 ] s[i,i+2] s[i,i+2]也都出现过。(注意这是一个排列,所以在每个串中每个数仅出现一次)
同理
s
[
i
,
i
+
2
]
s[i,i+2]
s[i,i+2]如果都出现的话,当
s
[
i
−
1
,
i
]
s[i-1,i]
s[i−1,i]都出现的话,那么
s
[
i
−
1
,
i
+
2
]
s[i-1,i+2]
s[i−1,i+2]也都出现。 以此类推,我们发现只要判断每个
s
[
k
,
k
+
1
]
s[k,k+1]
s[k,k+1]是否出现就可以推出所有的情况。
从后往前做即可。
我们可以很容易的用一个
p
o
s
[
i
]
[
j
]
pos[i][j]
pos[i][j]记录在第
i
i
i个串中,数字
j
j
j出现的位置。现在我们从后往前枚举第一个串的每个数字,用¥len[i]记录串
[
i
,
n
]
,
[
i
+
1
,
n
]
…
[
n
,
n
]
[i,n],[i+1,n]\dots [n,n]
[i,n],[i+1,n]…[n,n]出现的次数,比如12345,我们先考虑数字5,这肯定在所有串中都出现,所以
l
e
n
[
5
]
=
1
len[5] = 1
len[5]=1,现在考虑数字4,如果45都出现,那么就相当于加上5出现次数和4的次数,即
l
e
n
[
4
]
=
l
e
n
[
5
]
+
1
len[4] = len[5]+1
len[4]=len[5]+1,再考虑数字3,如果34成立,那么345肯定成立,所以一旦34成立,那么相当于4出现的次数加上5出现的次数再上上3出现的次数len[3] = len[4]+1,否则len[3] = 1。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[15][maxn], pos[15][maxn];
int len[maxn];
int main() {
int n,m;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i)
for(int j = 1; j <= n; ++j) {
scanf("%d", &a[i][j]);
pos[i][a[i][j]] = j;
}
long long ans = 0;
for(int i = n; i >= 1; --i) {
len[i] = 1;
bool ok = true;
for(int j = 1; j < m; ++j) {
int p = pos[j][a[0][i]];
if(p+1 > n || i+1 > n || a[j][p+1] != a[0][i+1])
ok = false;
}
if(ok) len[i] = len[i+1]+1;
// cout << len[i] << endl;
ans += len[i];
}
printf("%lld\n", ans);
return 0;
}