Codeforces 526D Om Nom and Necklace (KMP)

http://codeforces.com/problemset/problem/526/D

题意

给定一个串 T,对它的每一个前缀能否写成 A+B+A+B+...+B+A+B+A+B+...+B+A 的形式(k +1个 Ak 个 B,均可为空串)

分析

官方题解

对于前缀P,我们可以把它拆成P=SSSS…SSSST,其中T是S的前缀。显然可以用KMP算法,时间复杂度是O(n)。

当T=S:P=SSS…S。假设S出现了R次。如果转换为ABABAB…ABABA的形式,A和B是由几个S组成,而且最后的A一定是P的一个后缀。由贪心算法,A的长度尽量小,所以A中S出现R mod Q次。B中S出现R/Q-R mod Q次。因为只需判断R/Q-R mod Q>=0即可

当T!=S:由于上一种方法类似,A可以是SSS…ST,因为它的长度尽量小,所以我们只需判断是否R/Q-R mod Q>0。

那为什么A中有R mod Q个S,B中有R/Q-R mod Q 个S?因为要将字符串P=SSSSSSS变成ABABABABA的形式,可以看成是Q份AB和一份A结合,那么每份AB就有R/Q个S,所以剩下的R%Q就是A的S,于是B就是R/Q-R mod Q了。

当T=S时,B可以为空串,也就是B的长度可以为0,于是取等号。当T!=S时,反之。

#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#define rep(i,e) for(int i=0;i<(e);i++)
#define rep1(i,e) for(int i=1;i<=(e);i++)
#define repx(i,x,e) for(int i=(x);i<=(e);i++)
#define X first
#define Y second
#define PB push_back
#define MP make_pair
#define mset(var,val) memset(var,val,sizeof(var))
#define scd(a) scanf("%d",&a)
#define scdd(a,b) scanf("%d%d",&a,&b)
#define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define pd(a) printf("%d\n",a)
#define scl(a) scanf("%lld",&a)
#define scll(a,b) scanf("%lld%lld",&a,&b)
#define sclll(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
#define IOS ios::sync_with_stdio(false);cin.tie(0)

using namespace std;
typedef long long ll;
template <class T>
void test(T a){cout<<a<<endl;}
template <class T,class T2>
void test(T a,T2 b){cout<<a<<" "<<b<<endl;}
template <class T,class T2,class T3>
void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;}
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
const ll mod = 200907;
int T;
void testcase(){
    printf("Case #%d: ",++T);
}
const int MAXN = 1e6+5;
const int MAXM = 30;
int n,k;
char s[MAXN];
int Next[MAXN];

void getNext(){
    int i,j;
    j=Next[0]=-1;
    i=0;
    while(i<n){
        while(-1!=j&&s[i]!=s[j]) j=Next[j];
        Next[++i]=++j;
    }
}
int check(int i){
    int d=i-Next[i]; //最小循环节
    int cnt = i/d; //循环节次数
    if(i%d==0){ //被整除
        return cnt/k-cnt%k>=0;  //
    }else return cnt/k-cnt%k>0;

}
int main() {
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    scdd(n,k);
    scanf("%s",s);
    mset(Next,0);
    getNext();
    for(int i=1;i<=n;i++){
        printf("%d",check(i));
    }
//    for(int i=0;i<=1+n;i++) printf("%d ",Next[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fht-litost/p/9296893.html