1. 程式人生 > 實用技巧 >[CF316G3]Good Substrings(廣義SAM)

[CF316G3]Good Substrings(廣義SAM)

題面

http://codeforces.com/contest/316/problem/G3

題解

前置知識

可以建出這n+1個字串(即s以及所有的p)的廣義SAM。

我們可以在廣義SAM上維護出各個SAM結點分別在\(s,p_1,p_2,…,p_n\)中的出現次數。下稱\(s=p_0\),並設\(l_0=1,r_0=inf\)

具體做法就是,首先把\(p_0\)\(p_n\)建成一棵trie,然後在trie上進行BFS。若當前BFS到的trie節點u,是\(p_{i_1},p_{i_2},…,p_{i_m}\)的字首(也就是說,有且僅有\(p_{i_1},…,p_{i_m}\)

的終止節點在trie中u的子樹內),那麼就把trie上\(<fa[u],u>\)這條邊上的字元加入到SAM內,加入完成後其對應的SAM節點為loc[u],則把\(f[loc[u]][i_1],f[loc[u]][i_2],…,f[loc[u]][i_m]\)均++。(f是我們開的一個數組,初始值都是0)

然後,在SAM的fail樹上自底向上DFS,每次執行

for(int k = 0;k <= n;k++)f[fail[u]][k] += f[u][k];

最後得到的\(f[i][j]\)就表示SAM節點i在\(p_j\)中出現的次數啦。

統計答案時,只需計入滿足所有\(l_j<f[i][j]<r_j(0{\leq}j{\leq}n)\)

的i即可。

總時間複雜度\(O(n(\sum{|p_i|}+|s|))\)

程式碼

#include<bits/stdc++.h>

using namespace std;

#define rg register
#define In inline
#define ll long long

const int N = 50000;
const int SN = 550000;

In int read(){
	int s = 0,ww = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
	return s * ww;
}

int n,l[10+5],r[10+5],loc[SN+5];
char s[N+5];

struct SAM{
	int cnt,nx[2*SN+5][26],len[2*SN+5],fail[2*SN+5];
	int f[2*SN+5][11];
	void clear(){
		fail[0] = -1;
	}
	int extend(int last,int id){
		int cur = ++cnt;
		int p;
		len[cur] = len[last] + 1;
		for(p = last;p != -1 && !nx[p][id];p = fail[p])nx[p][id] = cur;
		if(p == -1)fail[cur] = 0;
		else{
			int q = nx[p][id];
			if(len[q] == len[p] + 1)fail[cur] = q;
			else{
				int clone = ++cnt;
				len[clone] = len[p] + 1;
				fail[clone] = fail[q];
				memcpy(nx[clone],nx[q],sizeof(nx[clone]));
				fail[cur] = fail[q] = clone;
				for(;p != -1 && nx[p][id] == q;p = fail[p])nx[p][id] = clone;
			}
		} 
		return cur;
	}
	In ll count(int u){
		return (ll)len[u] - (ll)len[fail[u]];
	}
	vector<int>link[2*SN+5];
	void dfs(int u){
		for(rg int i = 0;i < link[u].size();i++){
			int v = link[u][i];
			dfs(v);
			for(rg int k = 0;k <= n;k++)f[u][k] += f[v][k];
		}
	}
	void solve(){
		ll ans = 0;
		for(rg int i = 1;i <= cnt;i++)link[fail[i]].push_back(i);
		dfs(0);
		for(rg int i = 1;i <= cnt;i++){
			ll b = 1;
			for(rg int j = 0;j <= n;j++)if(f[i][j] < l[j] || f[i][j] > r[j]){
				b = 0;break;
			}
			ans += b * count(i);
		}
		cout << ans << endl;
	}
}S;

struct Trie{
	int nx[SN+5][26];
	vector<int>flag[SN+5];
	int cnt;
	void insert(char s[],int id){
		int ls = strlen(s + 1);
		int u,i;
		for(u = 0,i = 1;i <= ls;i++){
			if(!nx[u][s[i]-'a'])nx[u][s[i]-'a'] = ++cnt;
			u = nx[u][s[i]-'a'];
			flag[u].push_back(id);
		}
	}
	queue<int>q;
	void build(){
		S.clear();
		q.push(0);
		while(!q.empty()){
			int u = q.front();
			q.pop();
			for(rg int i = 0;i < 26;i++)if(nx[u][i]){
				int v = nx[u][i];
				loc[v] = S.extend(loc[u],i);
				q.push(v);
				for(rg int j = 0;j < flag[v].size();j++)
					S.f[loc[v]][flag[v][j]]++;
			}
		}
	}
}T;

int main(){
	scanf("%s",s + 1);
	T.insert(s,0);
	l[0] = 1,r[0] = N;
	n = read();
	for(rg int i = 1;i <= n;i++){
		scanf("%s",s + 1);
		l[i] = read(),r[i] = read();
		T.insert(s,i);
	}
	T.build();
	S.solve();
	return 0;
}