• @atcoder



    @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@

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

  • 相关阅读:
    计蒜客 奇怪的国家
    计蒜客 泥塑课
    计蒜客 判断质数
    hiho #1143 : 骨牌覆盖问题·一 (运用快速幂矩阵)
    二叉树建立,先序、中序、后序遍历(c实现)
    hiho #1272 买零食 [Offer收割]编程练习赛2
    hiho #1283 hiho密码 [Offer收割]编程练习赛3
    hiho #1288 微软2016.4校招笔试题 Font Size
    hiho一下 第九十八周 搜索一·24点
    hiho一下 第九十七周 数论六·模线性方程组
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12885512.html
Copyright © 2020-2023  润新知