系列文章目录
前言
我是傻逼
G. Song of the Sirens
给定n和q,字符串s0和t,t的长度为n
s
i
s_i
si满足
s
i
+
1
=
s
i
+
t
i
+
s
i
s_{i+1} =s_i+t_i+s_i
si+1=si+ti+si,加号表示字符串相接
q次询问,每次询问给出一个字符串
w
w
w,求
w
w
w作为子串在
s
i
s_i
si中的出现次数
记
f
(
i
,
w
)
f(i,w)
f(i,w)为
w
w
w在
s
i
s_i
si中的出现次数,
g
(
i
,
w
)
g(i,w)
g(i,w)为
w
w
w在
s
i
s_i
si中经过字符串中点
t
i
−
1
t_{i-1}
ti−1的出现次数
f
(
i
,
w
)
=
2
∗
f
(
i
−
1
,
w
)
+
g
(
i
,
w
)
f(i,w)=2*f(i-1,w)+g(i,w)
f(i,w)=2∗f(i−1,w)+g(i,w)
暴力构造出第一个长度大于等于
∣
w
∣
|w|
∣w∣的
s
i
d
s_{id}
sid
就能用字符串hash求
f
(
i
d
,
w
)
f(id,w)
f(id,w) 用来递推了
f
f
f的公式可以被改写为
f
(
i
,
w
)
=
2
k
−
i
d
∗
f
(
i
d
,
w
)
+
∑
i
=
i
d
+
1
k
g
(
i
,
w
)
∗
2
k
−
i
f(i,w)=2^{k-id}*f(id,w)+\sum_{i=id+1}^{k}g(i,w)*2^{k-i}
f(i,w)=2k−id∗f(id,w)+∑i=id+1kg(i,w)∗2k−i
注意到如果
t
i
=
t
j
t_i=t_j
ti=tj 且
i
,
j
>
i
d
i,j>id
i,j>id那么
g
(
i
,
w
)
=
g
(
j
,
w
)
g(i,w)=g(j,w)
g(i,w)=g(j,w)
这样就可以把g用前缀和优化掉
那么我们就改写一下g
原本没有用前缀和的时候,我们是通过hash求出
w
w
w在字符串
s
i
d
+
c
+
s
i
d
s_{id}+c+s_{id}
sid+c+sid中的出现次数
记为
h
[
c
]
h[c]
h[c]
然后求
∑
i
=
i
d
+
1
k
h
[
t
[
i
−
1
]
]
∗
2
k
−
i
\sum_{i=id+1}^{k}h[t[i-1]]*2^{k-i}
i=id+1∑kh[t[i−1]]∗2k−i
这个可以改写为
∑
c
=
0
25
h
[
c
]
∑
i
=
i
d
k
−
1
[
t
[
i
−
1
]
=
c
]
×
2
n
−
i
2
n
−
k
\frac{\sum_{c=0}^{25}h[c]\sum_{i=id}^{k-1}[t[i-1]=c]\times2^{n-i}}{2^{n-k}}
2n−k∑c=025h[c]∑i=idk−1[t[i−1]=c]×2n−i
于是后面那个
∑
\sum
∑就可以在
O
(
n
∗
26
)
O(n*26)
O(n∗26)时间内预处理
记
∑
j
=
1
i
[
t
[
j
−
1
]
=
c
]
×
2
n
−
i
=
s
u
m
[
c
]
[
i
]
\sum_{j=1}^{i}[t[j-1]=c]\times2^{n-i}=sum[c][i]
∑j=1i[t[j−1]=c]×2n−i=sum[c][i]
因为各种小问题调了两天
数组越界 初始化不完整 等等
刷了两页cf提交记录hhhhh
我是傻逼
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
const int maxn = 1e5 + 7, maxm = 1048576*4+500;
const ll md = 1e9 + 7;
ull B = 233;
int n, q, k;
string S, t, w;
ll sum[26][maxn], pw[maxn];
ull hpw[maxm], hs[maxm], hsw[maxm];
ll h[26];
vector<string> s;
void read(string &p) {
static char input[maxm];
scanf("%s", input);
p = input;
}
ull calc(int l, int r, ull *in) {
if (l == 0) return in[r];
return in[r]-in[l-1]*hpw[r-l+1];
}
ll ksm(ll a, ll b) {
ll res = 1ll;
a %= md;
while (b) {
if (b & 1ll)
res = res * a % md;
a = a * a % md;
b >>= 1ll;
}
return res % md;
}
void init() {//预处理O(n)
pw[0] = hpw[0] = 1;
for (int i = 1; i <= n; i++) {
pw[i] = pw[i-1] * 2ll % md;
hpw[i] = hpw[i-1] * B;
}
for (int i = n + 1; i < maxm; i++) hpw[i] = hpw[i-1] * B ;
for (int i = 0; i < 26; i++) {
sum[i][1] = pw[n-1]*(t[0] == i + 'a') % md;
for (int j = 1; j < n; j++) {
sum[i][j+1] = (sum[i][j] + pw[n-j-1]*(t[j] == i + 'a')) % md;
}
}
}
ll sol(int k, string &w) {
//sum w <= 1e6
int id = 0, m = w.size();
s.clear();
s.push_back(S);
while (s[id].length() < m) {
s.push_back(s[id]+t[id]+s[id]);
id++;
if (id > k) return 0;
}
//if (q == 1)
// printf("id == %d k == %d m == %d ", id, k, m);
hsw[0] = w[0]-'a';
for (int i = 1; i < m; i++)
hsw[i] = hsw[i-1] * B + w[i]-'a';
hs[0] = s[id][0]-'a';
for (int i = 1; i < s[id].size(); i++)
hs[i] = hs[i-1] * B + s[id][i]-'a';
ll cntf = 0;
for (int i = m-1; i < s[id].size(); i++) {
cntf += (hsw[m - 1] == calc(i-m+1, i, hs));
}
//if (q==1)
// printf("cur == %d m == %d\n", s[id].length(), m);
ll ans = cntf * pw[k-id] % md;
for (int i = 0; i < 26; i++) {
string tmp = s[id] + (char)(i + 'a') + s[id];
int cur = tmp.length() / 2;
hs[cur-m] = tmp[cur-m]-'a';
for (int j = cur-m+1; j < cur + m; j++)
hs[j] = hs[j-1] * B + tmp[j] - 'a';
h[i] = 0;
for (int j = cur; j < cur + m; j++) {
h[i] += (calc(j-m+1,j,hs) == hsw[m-1]);
h[i] %= md;
}
}
ll ans2 = 0;
for (int i = 0; i < 26; i++) {
ans2 = (ans2 + h[i] * (sum[i][k] - sum[i][id] + md) % md) % md;
}
ans2 = ans2 * ksm(pw[n-k], md-2) % md;
//if (q == 1) return 683796556;
return (ans + ans2) % md;
}
int main() {
cin >> n >> q;
read(S); read(t);
init();
int tt = q;
while (tt--) {
cin >> k;
read(w);
cout << sol(k, w) << "\n";
}
return 0;
}
本文介绍了如何处理字符串子串计数问题,涉及动态规划和字符串hash技巧。通过预处理前缀和优化,将复杂度降低,实现高效求解。文章还分享了在实现过程中遇到的调试细节,如数组越界和初始化问题。

505

被折叠的 条评论
为什么被折叠?



