题目大意
定义一个f(s)为将字符串s后面加上长度最小的串,使得新串仍时一个形如AA的串。在操作10^18次后求[L,R]中各个字母出现的次数。保证原串形如AA。
题解
首先操作可以看做是操作无限次
我们考虑每一次后面新添加的字符串T满足什么样的条件
一开始的S是形如s1s2s3...sns1s2s3...sns1s2s3...sns1s2s3...sn的,在操作一次之后变成了
s1s2s3...sns1s2s3...sc...snt1t2...tps1s2s3...sns1s2s3...sc...snt1t2...tp,其中sc是第一个字符串的最后一个字符
t是可以随便加的,现在的关键是要满足s[c+1]—-s[n]==s[1]—-s[n-c],然后我们现在要找到一个最小的c
这时我们想到了border
border是什么?
border是s的一个前缀,满足在复制若干次后可以包含s(最后一段可以溢出s,也就是说border的长度不一定是s长度的因子)
如果我们把第二段的一个border砍给第一段,那么剩下的第二段的s仍能够与第一段匹配,直到第二段的末尾(可以玩一个abcabcab,最小border是abc)
因为最终的字符串无限长,我们可以只看第一个字符串
那么本题就相当于每一次找当前字符串的最小border(设其长度为k),然后接到当前字符串的后面去
当k不为|S|的因子时发现字符串的形式是这样的:
s–>st–>sts–>stsst–>……
长的就像是斐波那契一样
可以用一个简单第递归方法计算,因为l,r都小于10^18,大概只是斐波那契的80项左右
k为|S|的因子时
s–>st–>stt–>sttt–>….
这个就可以直接计算了
还有一个问题,就是t怎么求
其实t=n-ne[n],其中n为s长度,ne[]是kmp中自我匹配的那个数组
证明忘记了,以前讲过的
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=2e5+5;
ll a[maxn][28];
ll i,j,k,l,r,x,y,n,t;
char s[maxn];
int ne[maxn];
bool bz;
void ge_border(){
fo(i,2,n){
while (j && s[j+1]!=s[i]) j=ne[j];
if (s[j+1]==s[i]) j++;
ne[i]=j;
}
t=n-ne[n];
if (n%t==0) bz=true;
}
ll calc(ll x,int c){
if (x<=n) return a[x][c];
if (x<=2*n) return a[n][c]+a[x-n][c];
ll t1=n,t2=n+t,an1=a[n][c],an2=an1+a[t][c],t3,an3;
while (t1+t2<=x){
t3=t2; t2=t1+t2; t1=t3;
an3=an2; an2=an1+an2; an1=an3;
}
return an2+calc(x-t2,c);
}
ll ge_ans(ll x,int c){
if (x<=n) return a[x][c];
if (bz){
x=x-n; ll ans=a[n][c];
ans=ans+(x/t)*a[t][c]+a[x%t][c];
return ans;
}
return calc(x,c);
}
int main(){
// freopen("077f.in","r",stdin);
scanf("%s",s+1);
scanf("%lld%lld",&l,&r);
n=strlen(s+1)/2;
ge_border();
fo(i,1,n){
fo(j,1,26) a[i][j]=a[i-1][j];
a[i][s[i]-96]++;
}
fo(j,1,26) printf("%lld ",-ge_ans(l-1,j)+ge_ans(r,j));
return 0;
}