【NOI2018】你的名字

题目大意:

一开始给出字符串S。
Q次询问,每次给出l,r和字符串T。
问T有多少不同子串不是S[l…r]的子串。
∑ ∣ T ∣ &lt; = 1 e 6 , ∣ S ∣ , ∣ T ∣ &lt; = 5 e 5 \sum|T|&lt;=1e6,|S|,|T|&lt;=5e5 T<=1e6,S,T<=5e5

题解:

不同子串一类问题优先考虑SAM。

感觉这题考的更多是对SAM的理解。

首先思考当 l = 1 , r = ∣ S ∣ l=1,r=|S| l=1,r=S,即送的68分。

有两种做法:
一种是当做广义SAM来做,先把S的SAM建好,然后把T加进去,再暴力回撤。
这个做法无法改成满分的做法,但是很直观。

另一种是先把S和T的SAM建好。

然后考虑把T扔进去S的SAM跑。

假设现在跑完了 T [ 1.. t ] T[1..t] T[1..t],匹配到了点x。

那么显然 T [ 1.. t ] T[1..t] T[1..t]的长度∈[dep[x]+1…t]的后缀是没有在S中出现过的。

然后我们考虑T的SAM的第t个结尾点y。

T [ 1.. t ] T[1..t] T[1..t]的长度为 [ 1.. d e p [ f a [ y ] ] ] [1..dep[fa[y]]] [1..dep[fa[y]]]的后缀也不用我们考虑,因为它们必然也出现在了前面的前缀中,会在前面就被统计到。

这里就考察了一个理解,就是SAM中为什么会分成 y 和 f a [ y ] y和fa[y] yfa[y]两个点,因为一个y不足以表示 f a [ y ] fa[y] fa[y]和更小长度的后缀,它们在前面就出现过了,所以要区分成两个点。

那么我们对 [ d e p [ x + 1 ] . t ] [dep[x+1].t] [dep[x+1].t] [ d e p [ f a [ y ] ] + 1.. t ] [dep[fa[y]]+1..t] [dep[fa[y]]+1..t]取个交就行了,注意这两个dep是不同的,是在两个SAM上的。

然后再思考满分做法。

大概也是在S的SAM上跑,我们要思考的问题是如何在S的SAM上跑出在S[l…r]的SAM上的feel

如果在跑完了 T [ 1.. t ] T[1..t] T[1..t]后,跑到了点x。

如果点x的Right集中没有一个右端点形成的串完全在 [ l . . r ] [l..r] [l..r]中,那么x就可以跳到fa[x],形成的串的长度当然取最小的 d e p [ f a [ x ] ] + 1 dep[fa[x]]+1 dep[fa[x]]+1

那么要维护Right集合中<=r的最大点,用线段树合并+线段树二分即可。

那么就可以维护出 T [ 1.. t ] T[1..t] T[1..t]最长的后缀出现在 S [ l . . r ] S[l..r] S[l..r]中了,剩下的做法和68分一样。

可以看出这道第三题的思维难度没有第二题大,且代码量适中,都是模板,没有什么细节,能否做出取决于在考场上是否能够冷静下来仔细分析。

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define pp printf
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
using namespace std;

const int N = 1e6 + 5;

char s[N]; int n, m;
int Q, l, r;

int ed[N];
struct tree {
	int l, r, s;
} t[N * 40];
int pl, pr, px, g[N], tt;
void add(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	if(!i) i = ++ tt;
	if(x == y) { t[i].s ++; return;}
	int m = x + y >> 1;
	add(t[i].l, x, m); add(t[i].r, m + 1, y);
	t[i].s = t[t[i].l].s + t[t[i].r].s;
}
void bin(int &i, int x, int y) {
	if(!x || !y) { i = x + y; return;}
	i = ++ tt;
	bin(t[i].l, t[x].l, t[y].l); bin(t[i].r, t[x].r, t[y].r);
	t[i].s = t[t[i].l].s + t[t[i].r].s;
}

void ft(int i, int x, int y) {
//	pp("%d %d %d %d\n", i, t[i].s, x, y);
	if(!i || y < pl || x > pr || !t[i].s) return;
	if(x == y) { px = x; return;}
	int m = x + y >> 1;
	ft(t[i].r, m + 1, y);
	if(!px) ft(t[i].l, x, m);
}

struct sam {
	int son[N][26], fa[N], dep[N], la, tot;
	#define push(v) dep[++ tot] = v
	void build() {
		fo(j, 0, 25) son[0][j] = 1;
		fo(i, 1, tot) memset(son[i], 0, sizeof son[i]);
		la = tot = 1;
	}
	void add(int c) {
		int p = la;
		push(dep[p] + 1); int np = tot;
		for(; p && !son[p][c]; p = fa[p]) son[p][c] = np;
		if(!p) fa[np] = 1; else {
			int q = son[p][c];
			if(dep[p] + 1 < dep[q]) {
				push(dep[p] + 1); int nq = tot;
				memcpy(son[nq], son[q], sizeof son[q]);
				fa[nq] = fa[q]; fa[q] = fa[np] = nq;
				for(; son[p][c] == q; p = fa[p]) son[p][c] = nq;
			} else fa[np] = q;
		}
		la = np;
	}
	int r[N], d[N];
	void dg(int x) {
		fo(i, 1, d[0]) pp("%c", 'a' + d[i]); pp("\n");
		fo(j, 0, 25) if(son[x][j]) {
			d[++ d[0]] = j;
			dg(son[x][j]);
			d[0] --;
		}
	}
	void nb() {
		fo(i, 1, tot) r[fa[i]] ++;
		fo(i, 1, tot) if(!r[i]) d[++ d[0]] = i;
		for(int i = 1; i <= d[0]; i ++) {
			int x = d[i];
			if(!(-- r[fa[x]])) d[++ d[0]] = fa[x];
			if(fa[x]) bin(g[fa[x]], g[fa[x]], g[x]);
		}
	}
} suf, sb;

ll ans;

int main() {
	freopen("name.in", "r", stdin);
	freopen("name.out", "w", stdout);
	scanf("%s", s + 1); n = strlen(s + 1);
	suf.build();
	fo(i, 1, n) {
		suf.add(s[i] - 'a');
		ed[i] = suf.la;
	}
//	suf.d[0] = 0; suf.dg(1);
	fo(i, 1, n) {
		pl = pr = i;
		add(g[ed[i]], 1, n);
	}
	suf.nb();
	scanf("%d", &Q);
	fo(ii, 1, Q) {
		scanf("%s", s + 1); m = strlen(s + 1);
		scanf("%d %d", &l, &r);
		pl = l; pr = r;
		ans = 0;
		sb.build();
		int x = 1, L = 0;
		fo(i, 1, m)	{
			int c = s[i] - 'a';
			sb.add(c);
			while(x && !suf.son[x][c]) x = suf.fa[x], L = suf.dep[x];
			x = suf.son[x][c]; L += x != 1;
			while(x > 1) {
				px = 0; ft(g[x], 1, n);
				if(!px || px - suf.dep[suf.fa[x]] < l) x = suf.fa[x], L = suf.dep[x]; else break;
			}
			int len = min(suf.dep[x], L);
			if(px == 0) len = 0; else {
				len = min(len, px - l + 1);
			}
			int y = sb.la;
			int st = sb.dep[sb.fa[y]];
			st = max(st, len);
			ans += max(0, sb.dep[y] - st);
		}
		pp("%lld\n", ans);
	}
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值