题解 [NOI2016] 优秀的拆分

题目传送门

Description

给出一个长度为 (n) 的字符串,问有多少个子串,满足可以写成 (AABB) 的形式。

(nle 3 imes 10^4)

Solution

md,直接 hash 暴力就能获得 (95) 分的好成绩。

考虑正解,我们设 (a_i) 表示以 (i) 结尾能写成 (AA) 形式的后缀个数,(b_i) 表示以 (i) 开头能写成 (AA) 形式的前缀个数。

我们考虑计算出长度为 (2k) 的字符串产生的贡献。我们每 (k) 个就设置一个端点,那么可以想到的是一个合法的字符串一定会经过且只经过两个相邻端点。

所以我们对于两个相邻端点考虑,如果两者的前缀的最长公共后缀 lcs 和两者的后缀的最长公共前缀 lcp 满足 (lcs+lcp-1ge k) 那么显然 (i-lcs+1sim i-lcs+1+lcs+lcp-1-k) 都是可以作为开头的点,那么就可以区间增加这一段的 (b)(a) 数组同理。

求 lcs 和 lcp 直接 SA 就好了。时间复杂度 (Theta(nlog n+nln n))

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 30005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

char s[MAXN];

struct SA{
	int n,m,num,x[MAXN],y[MAXN],c[MAXN],sa[MAXN],rk[MAXN],st[MAXN][21];
	void clear(){
		memset (x,0,sizeof (x));
		memset (y,0,sizeof (y));
	}
	int query (int l,int r){
		if (l == r) return n - sa[l] + 1;
		if (l > r) swap (l,r);++ l;
		int k = log2 (r - l + 1);
		return min (st[l][k],st[r - (1 << k) + 1][k]);
	} 
	void Build (){
		n = strlen (s + 1);
		m = 26;for (Int i = 1;i <= m;++ i) c[i] = 0;
		for (Int i = 1;i <= n;++ i) x[i] = s[i] - 'a' + 1,c[x[i]] ++;
		for (Int i = 1;i <= m;++ i) c[i] += c[i - 1];
		for (Int i = 1;i <= n;++ i) sa[c[x[i]] --] = i;
		for (Int k = 1;k <= n;k <<= 1){
			num = 0;for (Int i = n - k + 1;i <= n;++ i) y[++ num] = i;
			for (Int i = 1;i <= n;++ i) if (sa[i] > k) y[++ num] = sa[i] - k;
			for (Int i = 1;i <= m;++ i) c[i] = 0;for (Int i = 1;i <= n;++ i) c[x[i]] ++;for (Int i = 1;i <= m;++ i) c[i] += c[i - 1];
			for (Int i = n;i >= 1;-- i) sa[c[x[y[i]]] --] = y[i],y[i] = 0;swap (x,y),x[sa[1]] = num = 1;
			for (Int i = 2;i <= n;++ i) num += !(y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]),x[sa[i]] = num;
			m = num;if (m == n) break; 
		}
		for (Int i = 1;i <= n;++ i) rk[sa[i]] = i;
		for (Int i = 1,k = 0;i <= n;++ i){
			if (rk[i] == 1) k = 0;
			else{
				if (k) -- k;
				int j = sa[rk[i] - 1];
				while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) ++ k;
			}
			st[rk[i]][0] = k; 
		}
		for (Int j = 1;(1 << j) <= n;++ j) for (Int i = 1;i + (1 << j) - 1 <= n;++ i) st[i][j] = min (st[i][j - 1],st[i + (1 << j - 1)][j - 1]);
	}
	int fuckit (int a,int b){return query (rk[a],rk[b]);}
}S,T;

int n,a[MAXN],b[MAXN];

void Work (){
	S.clear(),T.clear();
	scanf ("%s",s + 1),n = strlen (s + 1);
	S.Build(),reverse (s + 1,s + n + 1),T.Build();
	memset (a,0,sizeof (a)),memset (b,0,sizeof (b));
	for (Int len = 1;(len << 1) <= n;++ len)
		for (Int i = len;i + len <= n;i += len){
			int l = i,r = i + len,L = n - (r - 1) + 1,R = n - (l - 1) + 1;
			int lcs = T.fuckit (L,R),lcp = S.fuckit (l,r);
			lcp = min (lcp,len),lcs = min (lcs,len - 1);
			if (lcs + lcp >= len){
				b[i - lcs] ++,b[i - lcs + (lcp + lcs - len + 1)] --;
				a[r + lcp - (lcp + lcs - len + 1)] ++,a[r + lcp] --;
			}
		}
	for (Int i = 1;i <= n;++ i) a[i] += a[i - 1],b[i] += b[i - 1];
	long long ans = 0;for (Int i = 1;i < n;++ i) ans += 1ll * a[i] * b[i + 1];
	write (ans),putchar ('
');
}

signed main(){
//	freopen ("P1117_6.in","r",stdin);
	int T;read (T);
	while (T --> 0) Work ();
 	return 0;
}
原文地址:https://www.cnblogs.com/Dark-Romance/p/14443979.html