border [arc077f]

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

题目大意

定义一个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;
}
* 函数功能: 寻找左侧圆弧拐点* 特殊说明: 无* 形 参: 无** 示例: Get_L_Arc_Turn_Point();* 返回值: 无*/uint8 L_Arc_Turn_Point[3][2] = {{0}}; //多找三个就足够使用uint8 L_Arc_Turn_Point_Flag = 0; //找到一个就挂出标志位uint8 L_Arc_Turn_Point_Num = 0; //记录找到的个数void Get_L_Arc_Turn_Point(void){ uint8 i = 0; L_Arc_Turn_Point_Flag = 0; for(i = 0; i < 3; i++) //每次调用先清零 { L_Arc_Turn_Point_Num = 0; L_Arc_Turn_Point[i][0] = 0; L_Arc_Turn_Point[i][1] = 0; } for(i =7; i < (L_Statics - 5); i++) { if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 7))) { if((L_Line[i][0] > L_Line[i - 7][0]) && (L_Line[i][0] > L_Line[i - 4][0]) && (L_Line[i][0] >= L_Line[i - 1][0]) && (L_Line[i][0] >= L_Line[i + 1][0]) && (L_Line[i][0] > L_Line[i + 3][0]) && (L_Line[i][0] > L_Line[i + 5][0]) && (L_Grow_Dir[i + 1] == -2 || L_Grow_Dir[i + 1] == 1) && (L_Grow_Dir[i + 3] == -2 || L_Grow_Dir[i + 3] == 1) && (L_Grow_Dir[i - 1] == 1 || L_Grow_Dir[i + 1] == 4) && (L_Grow_Dir[i - 3] == 1 || L_Grow_Dir[i - 3] == 4)) //左侧内凹 { L_Arc_Turn_Point[L_Arc_Turn_Point_Num][0] = L_Line[i][0]; L_Arc_Turn_Point[L_Arc_Turn_Point_Num][1] = L_Line[i][1]; L_Arc_Turn_Point_Flag = 1; L_Arc_Turn_Point_Num ++; i += 15; //每个圆弧拐点前后几个点可能都满足圆弧拐点的条件,所以找到后加15个点 if(L_Arc_Turn_Point_Num == 3) { break; } } } } for(i =7; i < (L_Statics - 5); i++) { if((L_Line[i][1] >= (Y_Border_Min + 2)) && (L_Line[i][1] <= (Y_Border_Max - 7))) { if((L_Line[i][0] < L_Line[i - 7][0]) && (L_Line[i][0] < L_Line[i - 4][0]) && (L_Line[i][0] <= L_Line[i - 1][0]) && (L_Line[i][0] <= L_Line[i + 1][0]) && (L_Line[i][0] < L_Line[i + 3][0]) && (L_Line[i][0] < L_Line[i + 5][0]) && (L_Grow_Dir[i + 1] == 4 || L_Grow_Dir[i + 1] == 1) && (L_Grow_Dir[i + 3] == 4 || L_Grow_Dir[i + 3] == 1) && (L_Grow_Dir[i - 1] == 1 || L_Grow_Dir[i + 1] == -2) && (L_Grow_Dir[i - 3] == 1 || L_Grow_Dir[i - 3] == -2)) //左侧外凸 { L_Arc_Turn_Point[L_Arc_Turn_Point_Num][0] = L_Line[i][0]; L_Arc_Turn_Point[L_Arc_Turn_Point_Num][1] = L_Line[i][1]; L_Arc_Turn_Point_Flag = 1; L_Arc_Turn_Point_Num ++; i += 15; if(L_Arc_Turn_Point_Num == 3) { break; } } } }} /*** 函数功能: 寻找右侧圆弧拐点* 特殊说明: 无* 形 参: 无** 示例: Get_R_Arc_Turn_Point();* 返回值: 无*/uint8 R_Arc_Turn_Point[3][2] = {{0}};uint8 R_Arc_Turn_Point_Flag = 0;uint8 R_Arc_Turn_Point_Num = 0;void Get_R_Arc_Turn_Point(void){ uint8 i = 0; R_Arc_Turn_Point_Flag = 0; for(i = 0; i < 3; i++) { R_Arc_Turn_Point_Num = 0; R_Arc_Turn_Point[i][0] = 0; R_Arc_Turn_Point[i][1] = 0; } for(i = 7; i < (R_Statics - 5); i++) { if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2))) { if((R_Line[i][0] < R_Line[i - 7][0]) && (R_Line[i][0] < R_Line[i - 4][0]) && (R_Line[i][0] <= R_Line[i - 1][0]) && (R_Line[i][0] <= R_Line[i + 1][0]) && (R_Line[i][0] < R_Line[i + 3][0]) && (R_Line[i][0] < R_Line[i + 5][0]) && (R_Grow_Dir[i + 1] == 4 || R_Grow_Dir[i + 1] == 1) && (R_Grow_Dir[i + 3] == 4 || R_Grow_Dir[i + 3] == 1) && (R_Grow_Dir[i - 1] == 1 || R_Grow_Dir[i + 1] == -2) && (R_Grow_Dir[i - 3] == 1 || R_Grow_Dir[i - 3] == -2)) //右侧内凹 { R_Arc_Turn_Point[R_Arc_Turn_Point_Num][0] = R_Line[i][0]; R_Arc_Turn_Point[R_Arc_Turn_Point_Num][1] = R_Line[i][1]; R_Arc_Turn_Point_Flag = 1; R_Arc_Turn_Point_Num ++; i += 15; if(R_Arc_Turn_Point_Num == 3) { break; } } } } for(i = 7; i < (R_Statics - 5); i++) { if((R_Line[i][1] >= (Y_Border_Min + 2)) && (R_Line[i][1] <= (Y_Border_Max - 2))) { if((R_Line[i][0] > R_Line[i - 7][0]) && (R_Line[i][0] > R_Line[i - 4][0]) && (R_Line[i][0] >= R_Line[i - 1][0]) && (R_Line[i][0] >= R_Line[i + 1][0]) && (R_Line[i][0] > R_Line[i + 3][0]) && (R_Line[i][0] > R_Line[i + 5][0]) && (R_Grow_Dir[i + 1] == -2 || R_Grow_Dir[i + 1] == 1) && (R_Grow_Dir[i + 3] == -2 || R_Grow_Dir[i + 3] == 1) && (R_Grow_Dir[i - 1] == 1 || R_Grow_Dir[i + 1] == 4) && (R_Grow_Dir[i - 3] == 1 || R_Grow_Dir[i - 3] == 4)) //右侧外凸 { R_Arc_Turn_Point[R_Arc_Turn_Point_Num][0] = R_Line[i][0]; R_Arc_Turn_Point[R_Arc_Turn_Point_Num][1] = R_Line[i][1]; R_Arc_Turn_Point_Flag = 1; R_Arc_Turn_Point_Num ++; i += 15; if(R_Arc_Turn_Point_Num == 3) { break; } } } }
最新发布
10-30
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值