【AtCoder Regular Contest 077 F】SS

KMP,找规律,border。

题目大意:

定义字符串 $S$ 是偶串,当且仅当它是两个完全相等的字符串拼接而成。如 abaabaaaaa 是偶串,qaqaq 不是偶串。

定义 $f(S)$ 为一个字符串,表示在 $S$ 后面添加最少的字符(至少 $1$ 个)使得它是偶串。可以证明当 $S$ 非空时,$f(S)$ 唯一存在。

现在给定偶串 $S$,求 $f(f(f(\cdots f(S))))$(迭代了足够多次)的第 $l\sim r$ 位中,每个字符的出现次数。

题解:

我们考虑用 SS 表示原来的 $S$(即考虑偶串的两个部分)。

通过打表找规律等方式可以发现一些性质:当 S 不由某个字符串循环两次以上组成时,令 TS 去掉它的 border 后的前缀(比如:若 Saba,则 Tab)。那么经过一次迭代,S 就变成了 ST

这个其实也不难理解,相当于 SS 去掉 S 的 border 之后翻倍,这个 border 就变成了第二个循环的开头。

那么我们继续迭代会怎么样呢?由于 TS 的前缀,所以 T 就变成了新一轮的 border(不可能更长),那么字符串就变成 STS。接下来就是 STSSTSTSSTSTS,$\ldots$。

可以发现这是斐波那契数列的增长方式,所以在 $O(\log n)$ 次迭代之后就会到达上限。故我们可以暴力求出每次迭代完后每个字符的出现次数。

由于前面和后面是一样的,所以直接从长的到短的贪心地匹配即可。

至于 S 由某个字符串循环两次以上组成的情况,最后显然是那个循环节不停地加。这种情况就非常简单了。

用 $[1,R]$ 的答案减去 $[1,L-1]$ 的答案即可。

Code:

#include<cstdio>
#include<cstring>
const int N=255555;
typedef long long LL;
struct data{
    LL len,a[26];
    inline void operator+=(const data&rhs){
        len+=rhs.len;
        for(int i=0;i<26;++i)a[i]+=rhs.a[i];
    }
    inline data operator-(const data&rhs)const{
        data c;
        c.len=0;
        for(int i=0;i<26;++i)c.a[i]=a[i]-rhs.a[i];
        return c;
    }
}d[500],ans;
char s[N];
int fail[N],n;
LL L,R;
void getfail(){
    for(int i=2,j=0;i<=n;++i){
        while(j&&s[j+1]!=s[i])j=fail[j];
        if(s[j+1]==s[i])++j;
        fail[i]=j;
    }
}
data solve(LL R){
    data ret;
    for(int i=ret.len=0;i<26;++i)ret.a[i]=0;
    if(n%(n-fail[n])==0){
        LL k=R/n;
        for(int i=1;i<=n;++i)ret.a[s[i]-'a']+=k;
        for(int i=1;i<=R%n;++i)++ret.a[s[i]-'a'];
    }else{
        memset(d,0,sizeof d);
        int tot=2;
        d[2].len=n;
        for(int i=1;i<=n;++i)++d[2].a[s[i]-'a'];
        d[1].len=n-fail[n];
        for(int i=1;i<=n-fail[n];++i)++d[1].a[s[i]-'a'];
        while(d[tot].len<=R){
            ++tot;
            d[tot]=d[tot-1],d[tot]+=d[tot-2];
        }
        for(int i=tot;i;--i)
        if(R>=d[i].len)R-=d[i].len,ret+=d[i];
        for(int i=1;i<=R;++i)++ret.a[s[i]-'a'];
    }
    return ret;
}
int main(){
    scanf("%s%lld%lld",s+1,&L,&R);
    n=strlen(s+1)>>1;
    getfail();
    ans=solve(R);
    ans=ans-solve(L-1);
    for(int i=0;i<26;++i)
    printf("%lld ",ans.a[i]);
    return 0;
}