【题解】Codeforces316G Good Substrings 后缀自动机,多串

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

n ( 10 ) n(10) 个规则,每个规则由字符串 p p 以及整数对 l , r l,r 构成。

如果一个字符串 t t p p 中的出现次数在 [ l , r ] [l,r] 内,就说明 t t 满足了规则 < p , l , r > <p,l,r> .

如果一个字符串满足了所有 n n 个规则,就说明它是好的。

给定一个字符串 s s ,问它的不同好子串数量。

这个问题有分级: p , s p,s 的长度在 200 / 2000 / 50000 200/2000/50000 以内,分别对应 1700 / 2200 / 2800 1700/2200/2800 难度.


对于长度在 2000 2000 以内的情况,可以对 n n p p 串和 s s 串都建立后缀自动机。

s s a m s的sam 遍历所有路径(不同子串),将经过的字符分别插入到 p p 串的后缀自动机中,即可知道 s s 的每个子串在每个 p p 串中出现的次数。

对于长度在 50000 50000 以内的情况,可以把所有字符串加上分隔符连接起来,然后统计每个节点在所有串中的出现次数,最后计数一遍就好。


困了。

时刻注意,一个节点的贡献不是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;
}
发布了375 篇原创文章 · 获赞 305 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/103154744