字符串(String)
字符串(String)
为了庆祝祖国生日, 小Z学起了斐波那契数列。
\(F[0]=0\)
\(F[1]=1\)
\(F[i]=F[i-2]+F[i-1]\)小Z突发奇想, 要是这个 \(F\) 是一个 string 类型该多有趣。
\(S[0]=”0”\)
\(S[1]=”1”\)
\(S[i]=S[i-2]S[i-1]\) (表示连接两个字符串)。小Z经过科学的计算后发现\(S[N]\)会很长很长, 但是他只想知道一个问题的答案, 就是小Z心中的0/1串\(T\)在\(S[N]\)中出现了多少次。
模\(P\)。
Input
第一行三个整数 \(N,M,P,\) \(N\) 如题中所述、 \(M\) 为串 \(T\) 的长度、 \(P\) 为需要取模的数。
第二行为一个长度为 \(M\) 的 0/1 串。
Output
仅包含一个整数, 为出现次数模 \(P\) 之后的值。
Example
输入 #1
\(7\) \(3\) \(100\)
\(101\)输出 #1
\(8\)
Scoring
对于 30%的数据,满足 \(N≤20\);
对于 60%的数据,满足 \(N≤10^5, M≤ܯ200\);
对于 100%的数据,满足 \(N≤10^9, M≤10000,P≤10^9\).
比赛时候把正解想出来了其实(小声bb
奈何基本功不过关,写挂了就拿了20分(
没啥好解释就是菜
——————————————————————————————————————————
30分做法:
暴力:/
60分做法:
把给定字符串叫str, 每次新合成的字符串是s[i],用一个数组p[i]在存储str在s[i]中的出现次数
你在写暴力的过程中会注意到一个现象,那就是每次两段加起来之后,规定str在s[i]中的出现次数总是等于p[i-2]+p[i-1]+连接处的新出现次数
那么我们每次只要判断新连接起来的部分有妹有str就好了,因此只要存s[i-2]的后m-1个和s[i-1]的前m-1个,然后看看加上新出现的次数就好了
然后,奇怪的性质出现了
0, 1, 01, 101, 01101, 10101101,0110110101101, ...
当\(s[i-2].length()>= m-1\) 时,我们把每次新连起来的部分提取出来
这里以样例中的m=3为例
0, 1, 01, 101, 01101, 10101101,0110110101101, 101011010110110101101, ...
相隔的是相同的,可以自行证明一下(我讲不清楚)
那么只要把这两部分中str的次数分别算出来,就是一个递推式了
转换一下(i为奇数与偶数时,e1与e2代表的东西不同,但对化简结果不影响
下面式子带入上面的,得到
转化后得
因此拿60分的代码流程:
前面暴力推字符串,推到\(s[i].length()>=m\) && \(s[i-1].length()>=m\)(s[i].length()=Fib[i], 增长的很快)
然后截出两段连接处的字符串, 求出e1与e2
快乐递推——
打完一交,袜,TLE了
100分做法:
我不会
一个递推式T了,咋办咯
用矩阵快速幂被
好了讲完了
另:由于本人太菜还没订正出来,这里放上gs大佬的代码一份
//%%%hgs%%%
#include <bits/stdc++.h>
int zjc;
struct Matrix {
int n, m, a[4][4];
int *operator[](const int &x) { return a[x]; }
Matrix() { memset(a, 0, sizeof(a)); }
void set() { memset(a, 0, sizeof(a)); }
void set(int _n, int _m) { n = _n; m = _m; memset(a, 0, sizeof(a)); }
void Init() { memset(a, 0, sizeof(a)); for (int i = 0; i < m; i++) a[i][i] = 1; }
friend Matrix operator * (Matrix &a, Matrix &b) {
Matrix c; c.set(b.n, a.m);
for (int i = 0; i < c.m; i++) {
for (int j = 0; j < c.n; j++) {
for (int k = 0; k < a.n; k++) {
c[i][j] += 1ll * a[i][k] * b[k][j] % zjc;
if (c[i][j] >= zjc) { c[i][j] -= zjc; }
}
}
}
return c;
}
} a, b, c, p, ret;
int n, m;
char M[20001];
int nxt[20001];
void getfail() {
nxt[0] = -1;
for (int i = 1, j = -1; M[i]; i++) {
while (~j && M[i] != M[j + 1]) { j = nxt[j]; }
if (M[i] == M[j + 1]) { ++j; }
nxt[i] = j;
}
}
int kmp(const char *s) {
int cnt = 0;
for (int i = 0, j = -1; s[i]; ++i) {
while (~j && s[i] != M[j + 1]) { j = nxt[j]; }
if (s[i] == M[j + 1]) { ++j; }
if (j == m - 1) { ++cnt; j = nxt[j]; }
}
return cnt;
}
int fib[22]; // val is the len of string.
std::string F[5];
int num[5];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
fib[0] = fib[1] = 1;
for (int i = 2; i <= 21; i++) { fib[i] = fib[i - 2] + fib[i - 1]; }
std::cin >> n >> m >> zjc >> M; getfail();
int _first = 0;
for (int i = 0; i <= 21; i++)
if (fib[i] >= m) { _first = i; break;
if (n < _first) { std::cout << 0 << '\n'; return 0;
F[0] = "0"; F[1] = "1";
for (int i = 2; i <= _first + 4; i++) { F[i % 5] = F[(i - 2) % 5] + F[(i - 1) % 5]; }
for (int i = 0; i < 5; i++) { num[i] = kmp(F[(_first + i) % 5].c_str()); }
if (_first + 4 >= n) { std::cout << num[n - _first] % zjc << '\n'; return 0; }
a.set(4, 4); a[0][0] = a[0][1] = a[1][0] = a[1][2] = a[3][3] = 1; a[3][0] = num[4] - num[3] - num[2];
b.set(4, 4); b[0][0] = b[0][1] = b[1][0] = b[1][2] = b[3][3] = 1; b[3][0] = num[3] - num[2] - num[1];
c.set(4, 1); c[0][0] = num[3], c[0][1] = num[2], c[0][2] = num[1], c[0][3] = 1;
p = a * b; ret.set(p.m, p.m); ret.Init();
int y = n - _first - 3 >> 1;
while (y) {
if (y & 1) { ret = ret * p; }
p = p * p; y >>= 1;
}
c = c * ret;
if (n - _first - 3 & 1) { c = c * a; }
std::cout << c[0][0] << '\n';
return 0;
}