BZOJ4566 [Haoi2016]找相同字符 【後綴數組】

題目

給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不一樣當且僅當這兩 個子串中有一個位置不一樣。ios

輸入格式

兩行,兩個字符串s1,s2,長度分別爲n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母算法

輸出格式

輸出一個整數表示答案數組

輸入樣例

aabb優化

bbaaspa

輸出樣例

10code

題解

先考慮暴力怎麼作 咱們枚舉兩個串的各自一個後綴suffix(i)和suffix(j) 則他們對答案的貢獻是LCP(suffix(i),suffix(j)) 如此獲得一個$O(n^3)$的算法字符串

固然若是你知道後綴數組,能夠$O(1)$求LCP,能夠優化到$O(n^2)$get

固然若是你知道後綴數組的套路,用一個單調棧掃一遍height[]能夠作到$O(nlogn)$【主要複雜度在求後綴數組】 具體這樣: 咱們知道,兩個後綴之間的LCP是他們之間height的最小值 若是給出一個位置的後綴,想求這個位置以前全部的後綴與這個位置的LCP之和之類的東西,因爲求最小值是一路過去的,因此前面的後綴的LCP不會比後面的大,因此總體是單調不降低的,能夠用單調棧處理string

最後咱們只須要分A、B串各用單調棧掃兩次統計出答案就能夠了it

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 400005,maxm = 100005,INF = 1000000000;
inline int read(){
	int out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
	return out * flag;
}
int sa[maxn],rank[maxn],height[maxn],t1[maxn],t2[maxn],bac[maxn],n,m;
char s[maxn];
int len;
LL ans;
void getSA(){
	int *x = t1,*y = t2; m = 256;
	for (int i = 0; i <= m; i++) bac[i] = 0;
	for (int i = 1; i <= n; i++) bac[x[i] = s[i]]++;
	for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
	for (int i = n; i; i--) sa[bac[x[i]]--] = i;
	for (int k = 1; k <= n; k <<= 1){
		int p = 0;
		for (int i = n - k + 1; i <= n; i++) y[++p] = i;
		for (int i = 1; i <= n; i++) if (sa[i] - k > 0) y[++p] = sa[i] - k;
		for (int i = 0; i <= m; i++) bac[i] = 0;
		for (int i = 1; i <= n; i++) bac[x[y[i]]]++;
		for (int i = 1; i <= m; i++) bac[i] += bac[i - 1];
		for (int i = n; i; i--) sa[bac[x[y[i]]]--] = y[i];
		swap(x,y);
		p = x[sa[1]] = 1;
		for (int i = 2; i <= n; i++)
			x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p);
		if (p >= n) break;
		m = p;
	}
	for (int i = 1; i <= n; i++) rank[sa[i]] = i;
	for (int i = 1,k = 0; i <= n; i++){
		if (k) k--;
		int j = sa[rank[i] - 1];
		while (s[i + k] == s[j + k]) k++;
		height[rank[i]] = k;
	}
}
int st[maxn],cnt[maxn],top;
LL cal[maxn];
void solve(){
	for (int i = 1; i <= n; i++){
		if (!height[i]) {top = 0; continue;}
		int tot = sa[i - 1] <= len ? 1 : 0;
		while (top && st[top] > height[i]) tot += cnt[top--];
		if (tot) st[++top] = height[i],cnt[top] = tot;
		cal[top] = cal[top - 1] + (LL)st[top] * (LL)cnt[top];
		if (sa[i] > len) ans += cal[top];
	}
	top = 0;
	for (int i = 1; i <= n; i++){
		if (!height[i]) {top = 0; continue;}
		int tot = sa[i - 1] > len ? 1 : 0;
		while (top && st[top] > height[i]) tot += cnt[top--];
		if (tot) st[++top] = height[i],cnt[top] = tot;
		cal[top] = cal[top - 1] + (LL)st[top] * (LL)cnt[top];
		if (sa[i] <= len) ans += cal[top];
	}
}
int main(){
	scanf("%s",s + 1); len = strlen(s + 1);
	s[len + 1] = '#';
	scanf("%s",s + len + 2);
	n = strlen(s + 1);
	getSA();
	solve();
	cout << ans << endl;
	return 0;
}
相關文章
相關標籤/搜索