给你一个字符串s, n次操作,每次把最后一个字母放到最前面放k次, 得到一个新的字符串,把新的字符串添加到原来的字符串的后面
m次询问,问你在l~r之间字母c出现的次数是多少
第i次变换得到的字符串长度是len[ i ] = len(s)*2^i,它的 前一半是i-1次的字符串,后一半是变换后的字符串
如果querylen < len[ i-1 ], 就直接递归到下一层
如果querylen>len[ i-1 ], 前一半是有基础串改变得到的,整体的字符个数不变,后一半是字符串的某一个后缀+递归到下一层计算
先预处理出每次变换后的字符串前缀,用cnt[64][110][30]记录每个字符出现的次数
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define ll __int64
char C[220000], P[105];
int num[30], o, n, m;
int K[100005], cnt[64][110][30];//长度最多是10^18,所以cnt的第一维开64就足够了
ll find(int n, ll p, int c)
{
if(!n)
{
int ans = 0;
for(int i = 1; i <= p; i++)
{
ans += (c==(P[i]-'a'));
}
return ans;
}
ll len = o*(1ll<<n-1);
if(p<len) return find(n-1, p, c);
return num[c]*(1ll<<n-1)+cnt[n][min((ll)K[n], p-len)][c]+find(n-1, max(0ll,p-len-K[n]), c);
}
int main()
{
scanf("%s", C+1);
int l = strlen(C+1);
for(int i = 1; i < l+1; i++)
{
P[i] = C[i];
num[C[i]-'a']++;
}
o = l;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)//预处理前缀
{
scanf("%d", &K[i]);
if(i <= 63)
{
K[i] %= l;
int res = 0;
for(int j = K[i]-1; j >= 0; j--)
{
res++;
for(int k = 0; k < 26; k++)
{
cnt[i][res][k] = cnt[i][res-1][k];
}
cnt[i][res][C[l-j]-'a']++;
}
int len = l;
for(int j = K[i]-1; j >= 0; j--)
{
C[++len] = C[l-j];
}
for(int j = 1; j <= l-K[i]; j++)
{
C[++len] = C[j];
}
if(l > 10000)//后期测试过,只要l>2300,这个代码就能ac
{
for(int j = 1; j <= l; j++) C[j] = C[j+l];
}
else l = len;
}
}
for(int i = 1; i <= n; i++)
{
if(o*(1ll<<i)>2e18)
{
n=i;
break;
}
}
for(int i = 1; i <= m; i++)
{
ll L, R;
char tmp[10];
scanf("%I64d%I64d%s", &L, &R, tmp);
printf("%I64d\n", find(n, R, tmp[0]-'a')-find(n, L-1, tmp[0]-'a'));
}
return 0;
}
介绍另一种方法
基本思路是一致的,不同点就是求前缀时,第二个方法更容易懂
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define ll __int64
#define inf 100005
const ll INF=1e18+10;
char s[110];
int k[inf], n, m;
ll lenth[70], pre[70][110][30], num[70][30];
int find(int x, ll len)
{
if(!x) return s[len]-'a';
if(len <= lenth[x-1]) return find(x-1, len);
len-=lenth[x-1];
if(k[x] >= len) return find(x-1, lenth[x-1]-k[x]+len);
return find(x-1, len-k[x]);
}
void init()
{
lenth[0] = strlen(s+1);
for(int i=1; i<=lenth[0]; i++)
{
for(int j=0; j<26; j++) pre[0][i][j] = pre[0][i-1][j];
pre[0][i][s[i]-'a']++;
}
for(int i=0; i<26; i++) num[0][i] = pre[0][lenth[0]][i];
for(int i=1; i<=n; i++)
{
lenth[i] = lenth[i-1]*2;
k[i]%=lenth[i-1];
for(int j=0; j<26; j++) num[i][j] = num[i-1][j]*2;
if(lenth[i] >= INF)
{
n = i;
break;
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=k[i]; j++)
{
for(int r=0; r<26; r++)
{
pre[i][j][r] = pre[i][j-1][r];
}
int c = find(i-1, lenth[i-1]-k[i]+j);
pre[i][j][c]++;
}
}
}
ll solve(int x, ll len, int c)
{
if(!len) return 0;
if(!x) return pre[0][len][c];
if(len <= lenth[x-1]) return solve(x-1, len, c);
len-=lenth[x-1];
if(len <= k[x]) return num[x-1][c]+pre[x][len][c];
return num[x-1][c]+pre[x][k[x]][c]+solve(x-1, len-k[x], c);
}
int main()
{
scanf("%s", s+1);
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
{
scanf("%d", &k[i]);
}
init();
while(m--)
{
ll l, r;
char c[10];
scanf("%I64d%I64d%s", &l, &r, c);
printf("%I64d\n", solve(n,r,c[0]-'a') - solve(n,l-1,c[0]-'a'));
}
return 0;
}