@atcoder - ARC077F@ SS


@description@

规定一个字符串为 “偶串” 当且仅当它可以表示成两个相同的字符串连接(如 "xyzxyz" 或 "aaaaaa")。

给定一个仅由小写字母组成的初始偶串 \(S_0\)。我们可以通过在 \(S_i\) 后加最少的字符(至少一个字符)得到新的偶串 \(S_{i+1}\),不难发现 \(S_{i+1}\) 是唯一存在的。

求在 \(S_{10^{100}}\) 中第 l 个字符到第 r 个字符中每个小写字母的出现次数。

传送门。

@solution@

考虑一个偶串 \(TT\) 怎么加最少的字符成为新的偶串 \(T'T'\):找到 \(T\) 的最大 border \(P\) 与最小周期 \(Q\),将 \(T\) 写作 \(Q + P\),则 \(T' = Q + P + Q\)

我们考虑已知 \(T\) 怎么快速求 \(T'\) 的最大 border:
如果 \(T\) 是循环串,则 \(T'\) 也是循环串,因此 \(T'\) 的最大 border 长度 = \(|T'| - |Q|\)
否则,\(T'\) 的最大 border 长度为 \(|Q|\)

关于第二条结论,首先注意到 \(T'\) 的最大 border 长度 \(\leq |P| + |Q|\)\(\geq |Q|\)
画一画发现假如 \(T'\) 的最大 border 长度为 L,则 \(T\) 中长度为 L 的前缀存在长度为 \(|Q|\) 的 border,根据弱周期引理可推出矛盾。

关于实现,前一种太简单了不讲;后一种可以发现 \(T_i\)\(T_{i-1}\)\(T_{i-2}\) 的拼接,长度呈斐波那契数列增长(跟指数级差不多),因此递归求解即可。
然后发现前一种情况可以直接用后一种情况的写法,减少代码量。

@accepted code@

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXN = 200000;

ll a[26], b[26], c[150][26], d[150];

char S[MAXN + 5]; int f[MAXN + 5];
int main() {
	int n, m; ll l, r;
	scanf("%s%lld%lld", S + 1, &l, &r);
	n = strlen(S + 1), m = n / 2;
	
	f[0] = -1, f[1] = 0;
	for(int i=2;i<=m;i++) {
		int j = f[i - 1];
		while( j != -1 && S[i] != S[j + 1] )
			j = f[j];
		f[i] = j + 1;
	}
	
	int p = m - f[m];
	for(int i=1;i<=p;i++) c[0][S[i] - 'a']++;
	for(int i=1;i<=m;i++) c[1][S[i] - 'a']++;
	int k; d[0] = p, d[1] = m;
	for(k=1;d[k]<=r;k++) {
		for(int j=0;j<26;j++)
			c[k + 1][j] = c[k][j] + c[k - 1][j];
		d[k + 1] = d[k] + d[k - 1];
	}
	
	ll t = r;
	for(int i=k;i>=0;i--) {
		if( t >= d[i] ) {
			for(int j=0;j<26;j++)
				a[j] += c[i][j];
			t -= d[i];
		}
	}
	for(int i=1;i<=t;i++)
		a[S[i] - 'a']++;
		
	t = l - 1;
	for(int i=k;i>=0;i--) {
		if( t >= d[i] ) {
			for(int j=0;j<26;j++)
				a[j] -= c[i][j];
			t -= d[i];
		}
	}
	for(int i=1;i<=t;i++)
		a[S[i] - 'a']--;
	
	for(int i=0;i<26;i++) printf("%lld ", a[i]);
}

@details@

当然关于实现,递推也可以,而且写起来也比较简单。

posted @ 2020-05-13 22:55  Tiw_Air_OAO  阅读(135)  评论(0编辑  收藏  举报