难度
− 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) l、2l、3l、⋯kl(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...sk−1 该串的后缀串 s n − k s n − k + 1 . . . s n − 1 s_{n-k}s_{n-k+1}...s_{n-1} sn−ksn−k+1...sn−1 相同。
数据范围
1 ≤ l ≤ n ≤ 1 0 5 1\le l\le n\le 10^5 1≤l≤n≤105
思路
-
直觉告诉我们
(没有直觉),这么有多 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 n−⌊ln⌋×l=n%l
因为红色块和红色块都是相同的,而且红色块之间有重叠部分。这个时候,可以想到:最终的循环块的长度为 gcd ( l , x ) \gcd(l,x) gcd(l,x)
怎么去证明呢?如果 x x x 的长度正好是 l l l 的约数,那么用几个蓝色块就可以填充一个红色块了。否则 x ⊥ l x\perp l x⊥l,那么我们最终的所有位置的元素都是相同的一个。 -
然后就有坑了。我们上述的思路是如果有长度为 2 l 2l 2l 的 b o r d e r border border。如果没有呢?
那我们的循环区间就变成了 n − l ( 等 价 于 n % l ) n-l(等价于 n\%l) n−l(等价于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;
}