【題解】Codeforces316G Good Substrings 後綴自動機,多串

字符串板刷 23/57,24/5723/57, 24/57

n(10)n(10)個規則,每個規則由字符串pp以及整數對l,rl,r構成。

如果一個字符串ttpp中的出現次數在[l,r][l,r]內,就說明tt滿足了規則<p,l,r><p,l,r>.

如果一個字符串滿足了所有nn個規則,就說明它是好的。

給定一個字符串ss,問它的不同好子串數量。

這個問題有分級:p,sp,s的長度在200/2000/50000200/2000/50000以內,分別對應1700/2200/28001700/2200/2800難度.


對於長度在20002000以內的情況,可以對nnpp串和ss串都建立後綴自動機。

ssams的sam遍歷所有路徑(不同子串),將經過的字符分別插入到pp串的後綴自動機中,即可知道ss的每個子串在每個pp串中出現的次數。

對於長度在5000050000以內的情況,可以把所有字符串加上分隔符連接起來,然後統計每個節點在所有串中的出現次數,最後計數一遍就好。


困了。

時刻注意,一個節點的貢獻不是1,而是它所表示的字符串數量。

有一個找了好長時間的bug,原來是因爲我的分隔符設定成z+1,z+2,z+3,...'z'+1,'z'+2,'z'+3,...,然後爆char,然後變成負數之後RE了。

分隔符設置成一樣是可以的,只要將其算入另外的字符串中。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 500016, MOD = 1000000007;

int sz, lst; //後綴自動機大小,上一次插入的節點
int ch[M<<1][40], len[M<<1], link[M<<1], cnt[12][M<<1];

void extend(const char *s, int id)
{
	for(int i=0; s[i]; ++i)
	{
		int c = s[i]-'a';
		int cur = ++sz;
		len[cur] = len[lst] + 1;

		int p = lst;
		while(!ch[p][c])
		{
			ch[p][c] = cur;
			p = link[p];
		}
		if(ch[p][c] != cur)
		{
			int q = ch[p][c];
			if(len[p] + 1 == len[q]) link[cur] = q;
			else
			{
				int clone = ++sz;
				memcpy(ch[clone], ch[q], sizeof(ch[q]));
				link[clone] = link[q];
				len[clone] = len[p]+1;
				while(ch[p][c]==q)
				{
					ch[p][c] = clone;
					p = link[p];
				}
				link[q] = link[cur] = clone;
			}
		}
		lst = cur;
		++cnt[id][cur];
	}
}
void build()
{
	vector<int> nodes;
	for(int i=1; i<=sz; ++i)
		nodes.push_back(i);
	sort(nodes.begin(), nodes.end(), [&](int a, int b){
		return len[a]>len[b];
	});
	for(auto u:nodes)
		for(auto cntx:cnt)
			cntx[link[u]] += cntx[u];
}

char str[M];
int lef[M], rig[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	scanf("%s", str); extend(str, 0);
	lef[0] = 1, rig[0] = 50000;
	int n = read();
	for(int i=1; i<=n; ++i)
	{
		string t = "{"; 
		extend(t.c_str(), n+1);
		scanf("%s", str);
		extend(str, i);
		lef[i] = read(), rig[i] = read();
	}
	build();

	int ans = 0;
	for(int i=1; i<=sz; ++i) 
	{
		int suc = 1;
		for(int j=0; j<=n; ++j) 
			if(cnt[j][i]<lef[j] || cnt[j][i]>rig[j])
				suc = 0, j=n+1;
		ans += suc*(len[i]-len[link[i]]);
	}
	printf("%d\n",ans );

    return 0;
}

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章