Codeforces Round #519D. Mysterious Crime(模擬+遞推)
阿新 • • 發佈:2018-11-01
題意
有m個長度為n的串,問其中公共的子串一共有多少個。
題解
這題第一想法是用字尾陣列lcp暴力計數,但這樣不是爆記憶體就是爆時間……仔細觀察可以發現,每個序列都是一個
的排列,我們可以以第一個串為樣式,將第一個串中的所有子串枚舉出來,然後依次判斷,這裡列舉需要一個技巧。
假設第一個串的
在每個串中都出現,那麼如果
在每個串中都出現的話,那麼
在每個串中也都出現過。所以我們只需判斷
和
是否成立就可以知道
的情況。從後往前列舉每一位數即可。
我們可以很容易的用一個
記錄在第
個串中,數字
出現的位置。現在我們從後往前列舉第一個串的每個數字,用¥len[i]記錄串
出現的次數,比如12345,我們先考慮數字5,這肯定在所有串中都出現,所以
,現在考慮數字4,如果45都出現,那麼就相當於加上5出現次數和4的次數,即
,再考慮數字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;
}