“kuangbin带你飞”专题计划——专题十六 KMP & 扩展KMP & Manacher

前言

个人总结字符串学习&总结(感觉主要是总结模板)

  1. 很久没刷专题了,有点后悔没有坚持下去。现在,把字符串&计算几何&DP都给刷一遍吧,其他的比如数论图论把那种很基础的算法弄的很熟悉就ok了。
  2. 专题传送门[kuangbin带你飞]专题1-23
  3. 感觉直接刷cf上的题单应该能帮助cf快速上分?codeforces上的一个题单(简化版)

题目(kmp算法我觉得应该称为前缀函数算法)

1. Number Sequence HDU - 1711 (KMP模板题:在另一个字符串中找一个字符串出现的位置)

  1. 传送门Number Sequence HDU - 1711
  2. 题意:字符串->数字(’#’->1e7),然后就是KMP模板了(在另一个字符串中找一个字符串第一次出现的位置)
  3. 代码

#include <iostream>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e6 + 10;
int n, m;
int a[N], b[N];

//在线算法:即,其实可以一个字符一个字符输入
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N];  //前缀数组

//得到前缀函数,即前缀数组的值
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
    
    
    for (int i = 1; i < l; i++) {
    
    
        int j = pi[i - 1];
        while (j > 0 && s[i] != s[j]) j = pi[j - 1];
        if (s[i] == s[j]) j++;
        pi[i] = j;
    }
}

//######################根据题目不同写不同的东西咯:

signed main() {
    
    
    int T;
    cin >> T;
    while (T--) {
    
    
        cin >> n >> m;
        for (int i = 0; i < n; i++) cin >> a[i];
        for (int i = 0; i < m; i++) cin >> b[i];
        int len = m;
        b[len++] = 1e7;
        for (int i = 0; i < n; i++) b[len++] = a[i];
        //注意是a中找b
        for (int i = 1; i < len; i++) {
    
    
            int j = pi[i - 1];
            while (j > 0 && b[i] != b[j]) j = pi[j - 1];
            if (b[i] == b[j]) j++;
            pi[i] = j;
        }
        // for (int i = 0; i < len; i++) cout << b[i] << " ";
        // cout << endl;
        // for (int i = 0; i < len; i++) cout << pi[i] << " ";
        // cout << endl;
        bool f = false;
        for (int i = 0; i < len; i++) {
    
    
            if (pi[i] == m) {
    
    
                f = true;
                cout << i - 2 * m + 1 << endl;
                break;
            }
        }
        if (!f) cout << -1 << endl;
    }
    return 0;
}

2. Oulipo HDU - 1686 (KMP模板题:求一个子串在另一个子串中出现的次数)

  1. Oulipo HDU - 1686
  2. 题意:求一个子串在另一个子串中出现的次数
  3. 题解&代码:(略)KMP模板题

3. 剪花布条 HDU - 2087 (KMP模板题:求另一个字符串中最多可以在剪出多少个该字符串)

  1. 剪花布条 HDU - 2087
  2. 题意:求另一个字符串中最多可以在剪出多少个该字符串
  3. 题解:kmp模板题,略了
  4. 代码:略了

4. Cyclic Nacklace HDU - 3746 (前缀函数模板题:求字符串周期n-pi[n-1])

  1. Cyclic Nacklace HDU - 3746
  2. 题意:求字符串周期
  3. 题解:n-pi[n-1]
  4. 代码:略,但是要注意
    1. C++就wa,G++就ac
    2. cin>>就TLE,scanf就TLE(cin>>1e7个数可能确实有点难受。不晓得正式赛的时候,等到TLE再用scanf来不来得及。习惯问题emmm

5. Period HDU - 1358 (前缀函数模板题:求字符串前缀的周期i+1-pi[i])

  1. Period HDU - 1358
  2. 题意&题解&代码:略

6. The Minimum Length HUST - 1010 (求周期水题:略了)

  1. The Minimum Length HUST - 1010

7. Power Strings POJ - 2406 (求周期水题+1,略了略了)

  1. Power Strings POJ - 2406

8. Seek the Name, Seek the Fame POJ - 2752 (深入理解前缀函数算法:打印字符串既是前缀也是后缀的所有前缀子串位置)

  1. Seek the Name, Seek the Fame POJ - 2752
  2. 题意:打印字符串既是前缀也是后缀的所有前缀子串位置
    1. 子串位置是起始位置,而且注意打印时的位置我们按下标从1开始
  3. 题解
    1. 求前缀函数的时候按下标从0开始
    2. 这个题涉及求前缀函数的算法原理
  4. 代码
#include <cstring>
#include <iostream>
#include <set>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 4e5 + 10;
char s[N];
int n;
//在线算法:即,其实可以一个字符一个字符输入
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N];  //前缀数组

//得到前缀函数,即前缀数组的值
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
    
    
    for (int i = 1; i < l; i++) {
    
    
        int j = pi[i - 1];
        while (j > 0 && s[i] != s[j]) j = pi[j - 1];
        if (s[i] == s[j]) j++;
        pi[i] = j;
    }
}

//######################根据题目不同写不同的东西咯:
void dfs(int p) {
    
    
    if (!p) return;
    dfs(pi[p - 1]);
    printf("%d ", p);
}
signed main() {
    
    
    while (scanf("%s", s) != EOF) {
    
    
        n = strlen(s);
        get(s, n);
        dfs(n);  //遍历顺序是从大到小,打印顺序是从小到大
        puts("");
    }
    return 0;
}

9. Blue Jeans POJ - 3080 (暴力水题:求不超过10个长度为60的串的最长公共子串)

  1. Blue Jeans POJ - 3080
  2. 题意:求不超过10个长度为60的串的最长公共子串,只要求长度大于等于3的子串(如果没有输出"no significant commonalities")。如果长度相等输出字典序最小的串。
  3. 题解:也许可以用xxx算法,但是能暴力还是暴力emm
  4. 代码
#include <iostream>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl;
using namespace std;
const int N = 15;
string s[N];
int n;
signed main() {
    
    
    int T;
    cin >> T;
    while (T--) {
    
    
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> s[i];
        string ans, str;
        // dbg(ans.size());
        // dbg(str.size());
        bool f;
        for (int len = 3; len <= 60; len++) {
    
    
            // dbg(len);
            for (int i = 0; i + len - 1 < 60; i++) {
    
    
                str = s[1].substr(i, len);  //枚举字符串
                // dbg(str);
                f = true;

                for (int j = 2; j <= n; j++) {
    
    
                    bool f1 = false;
                    for (int ii = 0; ii + len - 1 < 60; ii++) {
    
    
                        if (s[j].substr(ii, len) == str) {
    
    
                            f1 = true;
                            break;
                        }
                    }
                    if (!f1) {
    
    
                        f = false;
                        break;
                    }
                }
                if (f) {
    
    
                    // dbg(str);
                    if (ans.size() < len)
                        ans = str;
                    else
                        ans = min(ans, str);
                }
            }
        }
        if (ans.size() < 3)
            puts("no significant commonalities");
        else
            cout << ans << endl;
    }
    return 0;
}
/*
3
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
*/

10. Simpsons’ Hidden Talents HDU - 2594(kmp水题:略了)

  1. Simpsons’ Hidden Talents HDU - 2594

11. Count the string HDU - 3336(EKMP模板题:求所有前缀出现的次数||所有次数之和)

  1. Count the string HDU - 3336
  2. 题意:给一个字符串,求所有前缀出现的次数之和%10007
  3. 题解:使用EKMP中的z函数,然后求所有z[i]之和,注意z[0]=n
    1. z函数定义s[i~n-1]与s的最长公共前缀长度
    2. 比如z[2]=3,则表示s[2~n-1]包含了s[0],s[0~1],s[0~2]这三个前缀
  4. 代码
#include <iostream>
#include <string>
#define ll long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e5 + 10;

int n;
char s[N];
//首先约定:字符串下标从0开始
int z[N];  // z[i]定义:s和s[i,n-1]的最长公共前缀的长度。
//叫做z函数,特别的z[0]=0(也不一定,有时候题目就会暗示z[0]=n,注意变通就ok了)
void z_function(char *s, int n) {
    
    
    int l = 0, r = 0;  //算法的过程中我们维护右端点最靠右的匹配段[l,r]
    for (int i = 1; i < n; i++) {
    
    
        //如果i<=r,[i,r]和[i-l,r-l]与s的最长公共前缀应该相等
        if (i <= r && z[i - l] < r - i + 1) {
    
    
            z[i] = z[i - l];
        } else {
    
    
            z[i] = max(0, r - i + 1);
            while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
        }
        if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
        //注意每次需要O(1)更新一下下
    }
    z[0] = n;  //有时候题目会有这个要求,但是有时候就z[0]=0
}
signed main() {
    
    
    int T;
    cin >> T;
    while (T--) {
    
    
        scanf("%d%s", &n, s);
        for (int i = 0; i < n; i++) z[i] = 0;
        z_function(s, n);
        // for (int i = 0; i < n; i++) cout << ">>>" << i << " " << z[i] <<
        // endl;
        int ans = 0;
        for (int i = 0; i < n; i++) ans = (ans + z[i]) % 10007;
        printf("%d\n", ans);
    }
    return 0;
}

12. Clairewd’s message HDU - 4300 (暂时做不出来可以留着没事想想?但是下次的第一件事情应该就是做这件事情?)

  1. Clairewd’s message HDU - 4300
  2. 题意:不超过100个测试样例,每个测试样例有一个长度为26和一个长度不超过1e5的字符串。
    1. 每个测试样例,第一个字符串26个不同小写字母表示密文,对应明文"abcdefg…",比如"qwertyuiopasdfghjklzxcvbnm",q-a,w-b,…
    2. 第二个字符为某个情报,只截取了所有密文+部分明文(可能没有明文,也可能是完整明文)。
    3. 要求求出完整的最小可能长度的"密文+明文"。
    4. 样例:
      在这里插入图片描述
  3. 题解

猜你喜欢

转载自blog.csdn.net/I_have_a_world/article/details/119923583