【训练题33:字符串+数学】牛牛与字符串border | 2021牛客寒假算法基础集训营2

难度

− 28 162 -\frac{28}{162} 16228
理解思路了确实简单,字符串小白我就无法赛内开这种题了呜
这个榜可能被带的太偏了…

题意

  • 给一个只包含小写字母的长度为 n n n 的串。给你一个正整数 l l l
    你需要修改一些位置的字母,使得该串同时有长度为 l 、 2 l 、 3 l 、 ⋯ k l ( k l < n ) l、2l、3l、\cdots kl(kl<n) l2l3lkl(kl<n) b o r d e r border border

  • 所谓长度为 k   的   b o r d e r k\ 的\ border k  border,指的是该串的前缀串 s 0 s 1 . . . s k − 1 s_0s_1...s_{k-1} s0s1...sk1 该串的后缀串 s n − k s n − k + 1 . . . s n − 1 s_{n-k}s_{n-k+1}...s_{n-1} snksnk+1...sn1 相同。

数据范围

1 ≤ l ≤ n ≤ 1 0 5 1\le l\le n\le 10^5 1ln105

思路

  • 直觉告诉我们 (没有直觉) ,这么有多 b o r d e r border border 的话,该串大概率大部分子串都相同
    我们画一下图,看一下对于一般的情况的子串相同的情况:
    在这里插入图片描述

  • (1)首先,有一个长度为 l l l b o r d e r border border,所以 1 1 1 块和 1 ′ 1^\prime 1 块相同。
    其次,如果有长度为 2 l 2l 2l b o r d e r border border,那么 2 a 2_a 2a块和 2 a ′ 2_a^\prime 2a 块相同, 2 b 2_b 2b块和 2 b ′ 2_b^\prime 2b 块相同。
    但是由于 1 1 1 块和 2 a 2_a 2a 块是同一个块,且 1 ′ 1^\prime 1 块和 2 b ′ 2_b^\prime 2b 块是同一个块
    故: 2 a 2_a 2a 块和 2 b 2_b 2b 块和 2 a ′ 2_a^\prime 2a 块和 2 b ′ 2_b^\prime 2b 块都相同。
    以此类推,上图中每一个红色块都是相同的
    (2)再考虑到最后块放满了,还剩下一个 x x x
    这个块的长度易得为 n − ⌊ n l ⌋ × l = n % l n-\lfloor\frac{n}{l}\rfloor\times l=n\%l nln×l=n%l
    因为红色块和红色块都是相同的,而且红色块之间有重叠部分。这个时候,可以想到:最终的循环块的长度为 gcd ⁡ ( l , x ) \gcd(l,x) gcd(l,x)
    怎么去证明呢?如果 x x x 的长度正好是 l l l 的约数,那么用几个蓝色块就可以填充一个红色块了。否则 x ⊥ l x\perp l xl,那么我们最终的所有位置的元素都是相同的一个

  • 然后就有坑了。我们上述的思路是如果有长度为 2 l 2l 2l b o r d e r border border。如果没有呢?
    在这里插入图片描述
    那我们的循环区间就变成了 n − l ( 等 价 于 n % l ) n-l(等价于 n\%l) nl(n%l)
    注意,此时的循环区间(蓝色)不一定是 n n n 的因子,最后形成的串会多一个尾巴。

  • 那么怎么实现呢?
    对于一个循环区间,如果循环节大小为 g g g,那么易得 s 0 , s g , s 2 g . . . s_0,s_g,s_{2g}... s0,sg,s2g...他们的字符一定是一样的,同时 s 1 , s g + 1 , s 2 g + 1 . . . s_1,s_{g+1},s_{2g+1}... s1,sg+1,s2g+1... 他们的字符也是一样的。
    这个不就是 g g g 的完全剩余系吗?
    要求修改次数最少,我们就是求出对于所有位置 i i i,所有 s i % g s_{i\%g} si%g 的出现次数最多的是什么字符就可以了。

写完都要不认识 “块” 这个字了//

核心代码

时间复杂度: O ( 26 N ) O(26N) O(26N)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 1e5+50;

int cnt[MAX][30];
char ans[MAX];
string ss;
int main()
{
    
    
    int T;cin >> T;
    while(T--){
    
    
        int n,k;	/// k 为上述的 l
        cin >> n >> k;
        cin >> ss;
        int g;
        if(n / k != 1)g = __gcd(n,k);		/// 有多种   border
        else g = n - k;						/// 只有一种 border
        if(g == 0){
    
    
            cout << ss << endl;
            continue;
        }
        
        for(int i = 0;i < g;++i)
        for(int j = 0;j < 26;++j)
            cnt[i][j] = 0;

        for(int i = 0;i < ss.size();++i)	/// 算出最终循环节每个位置的字符出现次数
            cnt[i%g][ss[i]-'a']++;

        for(int i = 0;i < g;++i){
    
    
            int mx = 0;
            char ch = 'a';
            for(int j = 0;j < 26;++j){
    
    
                if(cnt[i][j] > mx){
    
    
                    mx = cnt[i][j];
                    ch = 'a' + j;
                }
            }
            ans[i] = ch;
        }
        for(int i = 0;i < ss.size();++i)	/// 取众数
            cout << ans[i%g];
        puts("");

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/113620044