码迷,mamicode.com
首页 > 其他好文 > 详细

字符串(String)

时间:2020-07-22 11:39:18      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:first   mem   ++   div   技术   cas   null   The   提取   

字符串(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的次数分别算出来,就是一个递推式了

\[\begin{cases} p[i]=p[i-1]+p[i-2]+e1 \ \ \ \ \ \ \text{if n 为奇数}\p[i]=p[i-1]+p[i-2]+e2 \ \ \ \ \ \ \text{if n 为偶数}\\end{cases}\]

转换一下(i为奇数与偶数时,e1与e2代表的东西不同,但对化简结果不影响

\[\begin{cases} p[i+1]=p[i]+p[i-1]+e1\p[i]=p[i-1]+p[i-2]+e2\\end{cases}\]

下面式子带入上面的,得到

\[p[i+1]=(p[i-1]+p[i-2]+e2)+p[i-1]+e1 \]

转化后得

\[p[i]=2*p[i-2]+p[i-3]+(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;
}

大佬的blog

字符串(String)

标签:first   mem   ++   div   技术   cas   null   The   提取   

原文地址:https://www.cnblogs.com/Sheffield/p/13358670.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!