border [arc077f]

本文介绍了一种关于字符串操作的问题解决方法,通过分析字符串的边界性质来确定如何扩展字符串以形成特定模式。讨论了不同情况下字符串扩展的行为,并提供了具体的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

定义一个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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值