1. 程式人生 > >Codeforces Round #519D. Mysterious Crime(模擬+遞推)

Codeforces Round #519D. Mysterious Crime(模擬+遞推)

題目連結

題意

有m個長度為n的串,問其中公共的子串一共有多少個。

題解

這題第一想法是用字尾陣列lcp暴力計數,但這樣不是爆記憶體就是爆時間……仔細觀察可以發現,每個序列都是一個 1 n 1\sim n 的排列,我們可以以第一個串為樣式,將第一個串中的所有子串枚舉出來,然後依次判斷,這裡列舉需要一個技巧。
假設第一個串的 [

i + 1 , i + k ] [i+1,i+k] 在每個串中都出現,那麼如果 [
i , i + 1 ] [i,i+1]
在每個串中都出現的話,那麼 [ i
, i + 2 ] , [ i , i + 3 ] , , [ i , i + k ] [i,i+2],[i,i+3],\dots,[i,i+k]
在每個串中也都出現過。所以我們只需判斷 s [ i ] s[i] s [ i + 1 ] s[i+1] 是否成立就可以知道 [ i , n ] [i,n] 的情況。從後往前列舉每一位數即可。
我們可以很容易的用一個 p o s [ i ] [ j ] pos[i][j] 記錄在第 i i 個串中,數字 j j 出現的位置。現在我們從後往前列舉第一個串的每個數字,用¥len[i]記錄串 [ i , n ] , [ i + 1 , n ] [ n , n ] [i,n],[i+1,n]\dots [n,n] 出現的次數,比如12345,我們先考慮數字5,這肯定在所有串中都出現,所以 l e n [ 5 ] = 1 len[5] = 1 ,現在考慮數字4,如果45都出現,那麼就相當於加上5出現次數和4的次數,即 l e n [ 4 ] = l e n [ 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;
}