牛牛与字符串border 题解(gcd)
题目链接
题目思路
我觉得官方题解写的很好,直接采用官方题解吧
当$2*k\leq n || k==n $
\(len=gcd(n,k)\)
否则
\(len=n-k\)
\(len\)代表循环节
简单证明
假设k<n/2
那很显然,根据题意一定会产生k和2k两个限制条件。根据border定义,此时圆圈部分等于圆圈部分,五角星部分等于五角星部分。但是问题来了,这是一个串,不是两个串,而这个后缀中五角星和圆圈是同一个位置,说明五角星和圆圈应该是同一个字符。
所以循环节一定是k的因数。
然后这个border再按照最长拉满。
此时这个循环节变成了n-n/k*k,这个表达式其实就是模除取余数。所以循环节不但是k的因子也同时是n%k的因子。
so,公因子里面选个最大的就好。
问题来了gcd(k,n%k)它是啥呢,它不就是gcd(n,k)。
假设k>=n/2
如果k比n的一半还大,那显然只有一个限制条件。然后如图所示吧,我觉得一看就懂。
代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const int eps=1e-6;
int n,k,len;
char s[maxn];
int asc[maxn][300];
int ma[maxn];
char ans[maxn];
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
ma[i]=0;
for(int j='a';j<='z';j++){
asc[i][j]=0;
}
}
scanf("%s",s+1);
if(2*k<=n||n==k){
len=gcd(n,k);
}else{
len=n-k;
}
for(int i=1;i<=n;i++){
int pos=(i-1)%len+1;
asc[pos][s[i]]++;
if(asc[pos][s[i]]>ma[pos]){
ma[pos]=asc[pos][s[i]];
ans[pos]=s[i];
}
}
for(int i=1;i<=n;i++){
printf("%c",ans[(i-1)%len+1]);
}
printf("\n");
}
return 0;
}
不摆烂了,写题