【NOI2019模拟2019.6.17】可爱

https://jzoj.net/senior/#contest/show/2775/1

题目大意:

给一个长度为n的字符串。

对每一个长度为m的子串,求其它长度为m的子串有多少个和它最多有一位不同。

1 < = m < = n < = 100000 1<=m<=n<=100000 1<=m<=n<=100000

题解:

最多有一位不同等价于两个串的 l c p + l c s > = m − 1 lcp+lcs>=m-1 lcp+lcs>=m1

先把串正着反着都建一遍SA。

把正串的 h e i g h t height height从大到小排序,依次加入,每次相当于合并两个段的串。

考虑启发式合并,枚举长度小的那一段的所有串,由于 h e i g h t height height是从大到小枚举的,所以当前的 h e i g h t height height就是 l c p lcp lcp

满足 l c s < = m − 1 − l c p lcs<=m-1-lcp lcs<=m1lcp的串在反串SA的一段区间内,用(线段树)二分找出这段区间。

之后相当于统计另一段的串在反串SA上对应的位置在这个区间的个数,这个可以用线段树求和,并且对应的,也要区间加。

也许需要写并查集、线段树合并。

时间复杂度 O ( n   l o g 2   n ) O(n~log^2~n) O(n log2 n),空间复杂度 O ( n   l o g   n ) O(n~log~n) O(n log n)

code:

#include
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(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 --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;

int a2[20], lg[N];

int n, m;
char s[N];

struct SA {
	char s[N];
	int n, m;
	int rk[N], sa[N], tp[N], tx[N], he[N];
	int f[17][N];
	void rsort() {
		fo(i, 1, m) tx[i] = 0;
		fo(i, 1, n) tx[rk[tp[i]]] ++;
		fo(i, 1, m) tx[i] += tx[i - 1];
		fd(i, n, 1) sa[tx[rk[tp[i]]] --] = tp[i];
	}
	int cmp(int *f, int x, int y, int z) { return f[x] == f[y] && f[x + z] == f[y + z];}
	void build() {
		s[0] = s[n + 1] = -1; tp[n + 1] = 0;
		fo(i, 1, n) tp[i] = i, rk[i] = s[i];
		m = 127; rsort();
		for(int p = 0, w = 1; p < n; w *= 2, m = p) {
			p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
			fo(i, 1, n) if(sa[i] > w) tp[++ p] = sa[i] - w;
			rsort();
			fo(i, 1, n) tp[i] = rk[i];
			rk[sa[1]] = p = 1;
			fo(i, 2, n) rk[sa[i]] = cmp(tp, sa[i - 1], sa[i], w) ? p : ++ p;
		}
		int j, k = 0;
		for(int i = 1; i <= n; he[rk[i ++]] = k)
			for( k ? k -- : 0, j = sa[rk[i] - 1]; s[i + k] == s[j + k]; k ++);
		fo(i, 1, n) f[0][i] = he[i];
		fo(j, 1, 16) fo(i, 1, n) {
			f[j][i] = f[j - 1][i];
			if(i + a2[j - 1] <= n) f[j][i] = min(f[j][i], f[j - 1][i + a2[j - 1]]);
		}
	}
	int qmin(int x, int y) {
		if(x > y) swap(x, y);
		int l = lg[y - x + 1];
		return min(f[l][x], f[l][y - a2[l] + 1]);
	}
} a, b;

int d[N];
int cmp(int x, int y) { return a.he[x] > a.he[y];}

int f[N]; vector<int> p[N];
int F(int x) { return f[x] == x ? x : (f[x] = F(f[x]));}
void bin(int x, int y) { f[F(x)] = F(y);}
#define si size()
#define pb push_back

ll ans[N];

struct P {
	int x, y;
	P(int _x = 0, int _y = 0) { x = _x, y = _y;}
};

P query(SA &a, int i, int m) {
	int x = a.rk[i], L = x, R = x;
	for(int l = 1, r = x - 1; l <= r; )	{
		int md = l + r >> 1;
		if(a.qmin(md + 1, x) >= m) L = md, r = md - 1; else l = md + 1;
	}
	for(int l = x + 1, r = n; l <= r; ) {
		int md = l + r >> 1;
		if(a.qmin(x + 1, md) >= m) R = md, l = md + 1; else r = md - 1;
	}
	return P(L, R);
}

struct tree {
	int l, r, x, c;
} t[N * 20];
#define i0 t[i].l
#define i1 t[i].r
int pl, pr, px, rt, tot;
void ad(int i, int x) { if(i) t[i].x += x;}
void down(int i) {
	if(t[i].x) {
		ad(i0, t[i].x);
		ad(i1, t[i].x);
		t[i].x = 0;
	}
}
void kq(int &i, int x, int y) {
	if(y < pl || x > pr) return;
	i = ++ tot;
	if(x == y) { t[i].c = 1; return;}
	int m = x + y >> 1;
	kq(i0, x, m); kq(i1, m + 1, y);
	t[i].c = t[i0].c + t[i1].c;
}
void add(int &i, int x, int y) {
	if(y < pl || x > pr || !i) return;
	if(x >= pl && y <= pr) {
		ad(i, px); return;
	}
	int m = x + y >> 1; down(i);
	add(i0, x, m); add(i1, m + 1, y);
	t[i].c = t[i0].c + t[i1].c;
}
void ft(int i, int x, int y) {
	if(y < pl || x > pr || !i) return;
	if(x >= pl && y <= pr) { px += t[i].c; return;}
	int m = x + y >> 1; down(i);
	ft(i0, x, m); ft(i1, m + 1, y);
}
void dg(int &x, int y) {
	if(!x) { x = y; return;}
	if(!y) return;
	down(x); down(y);
	dg(t[x].l, t[y].l);
	dg(t[x].r, t[y].r);
	t[x].c += t[y].c;
}
void sf(int i, int x, int y) {
	if(!i) return;
	if(x == y) {
		ans[n - b.sa[x] + 1 - (m - 1)] = t[i].x;
		return;
	}
	int m = x + y >> 1; down(i);
	sf(i0, x, m); sf(i1, m + 1, y);
}

int g[N];

int main() {
	freopen("lovely.in", "r", stdin);
	freopen("lovely.out", "w", stdout);
	a2[0] = 1; fo(i, 1, 16) a2[i] = a2[i - 1] * 2, lg[a2[i]] ++;
	fo(i, 1, 1e5) lg[i] += lg[i - 1];
	scanf("%d %d", &n, &m);
	scanf("%s", s + 1);
	fo(i, 1, n) a.s[i] = s[i], b.s[i] = s[n - i + 1];
	a.n = b.n = n;
	
	a.build(); b.build();
	
	fo(i, 1, n / 2) swap(b.rk[i], b.rk[n - i + 1]);
	
	fo(i, 2, n) d[i - 1] = i;
	sort(d + 1, d + n, cmp);
	fo(i, 1, n) {
		f[i] = i;
		if(i <= n - m + 1) {
			pl = pr = b.rk[i + m - 1];
			kq(g[i], 1, n);
			p[i].pb(i);
		}
	}
	fo(i, 1, n - 1) {
		int x = F(a.sa[d[i] - 1]), y = F(a.sa[d[i]]);
		int H = a.he[d[i]], oh = m - 1 - H;
		if(p[x].si > p[y].si) swap(x, y);
		ff(_j, 0, p[x].si) {
			int j = p[x][_j];
			P c = query(b, j + m - 1, oh);
			pl = c.x, pr = c.y; px = 1;
			add(g[y], 1, n);
			pl = c.x, pr = c.y; px = 0;
			ft(g[y], 1, n);
			pl = pr = b.rk[j + m - 1];
			add(g[x], 1, n);
		}
		dg(g[y], g[x]);
		bin(x, y);
		ff(_j, 0, p[x].si) p[y].pb(p[x][_j]);
	}
	sf(g[F(1)], 1, n);
	fo(i, 1, n - m + 1) pp("%lld ", ans[i]);
}

你可能感兴趣的:(线段树,Suffix,array,启发式算法)