牛牛与字符串border 题解(gcd)

题目链接

题目思路

我觉得官方题解写的很好,直接采用官方题解吧

当$2*k\leq n || k==n $

\(len=gcd(n,k)\)

否则

\(len=n-k\)

\(len\)代表循环节

简单证明

假设k<n/2

img

那很显然,根据题意一定会产生k和2k两个限制条件。根据border定义,此时圆圈部分等于圆圈部分,五角星部分等于五角星部分。但是问题来了,这是一个串,不是两个串,而这个后缀中五角星和圆圈是同一个位置,说明五角星和圆圈应该是同一个字符。

所以循环节一定是k的因数。

然后这个border再按照最长拉满。

img

img

此时这个循环节变成了n-n/k*k,这个表达式其实就是模除取余数。所以循环节不但是k的因子也同时是n%k的因子。

so,公因子里面选个最大的就好。

问题来了gcd(k,n%k)它是啥呢,它不就是gcd(n,k)。

假设k>=n/2

img

img

如果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;
}

posted @ 2021-03-01 11:04  hunxuewangzi  阅读(106)  评论(0编辑  收藏  举报