• KMP / EXKMP


    目录

    KMP

    字符串匹配

    最小循环覆盖 

    EXKMP

    字符串匹配

    Secret word 

    Password 


    KMP

    模板

    1. void kmp() {
    2. int m = strlen(p + 1);
    3. int j = 0;
    4. nxt[1] = 0;
    5. for (int i = 2; i <= m; ++i) {
    6. while (j && p[i] != p[j + 1])
    7. j = nxt[j];
    8. if (p[i] == p[j + 1])
    9. ++j;
    10. nxt[i] = j;
    11. }
    12. }

    p[i]  与  p[j + 1]  作比较? 

    j是与i - 1的字符相匹配的。所以但我们计算i时,待匹配的是j + 1 这个位置的字符。

    比如下方的第二个和第四个字符,都是b,他们是匹配的。i等于5之后,此时的j还是2,所以是i与j+1进行比较。

    用来解决字符串的匹配问题,比如在文档中  Ctrl + F 去查找特定的内容。

    需要清除nxt数组的含义:

                    a        b        a        b        a        c        m

                    0        0        1        2        3        0        0

    nxt[i]  以i结尾的字符,与p字符串前缀最大匹配的长度

    字符串匹配

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. char s[100005], p[100005];
    8. int nxt[100005], f[100005];
    9. int n, m;
    10. void solve() {
    11. scanf("%s%s", s + 1, p + 1);
    12. n = strlen(s + 1);
    13. m = strlen(p + 1);
    14. int j = 0;
    15. nxt[1] = 0;
    16. for (int i = 2; i <= m; ++i) {
    17. while (j && p[i] != p[j + 1])
    18. j = nxt[j];
    19. if (p[i] == p[j + 1])
    20. ++j;
    21. nxt[i] = j;
    22. }
    23. j = 0;
    24. for (int i = 1; i <= n; ++i) {
    25. while (j == m || (j && s[i] != p[j + 1]))
    26. j = nxt[j];
    27. if (s[i] == p[j + 1])
    28. ++j;
    29. f[i] = j;
    30. }
    31. int ans = 0;
    32. for (int i = 1; i <= n; ++i) {
    33. if (f[i] == m)
    34. ans++;
    35. }
    36. if (ans == 0) printf("-1\n-1\n");
    37. else {
    38. printf("%d\n", ans);
    39. for (int i = 1; i <= n; ++i) {
    40. if (f[i] == m) {
    41. printf("%d ", i - m + 1);
    42. }
    43. }
    44. puts("");
    45. }
    46. }
    47. int main(){
    48. int t; scanf("%d", &t);
    49. for (int i = 1; i <= t; ++i) {
    50. solve();
    51. }
    52. return 0;
    53. }

    最小循环覆盖 

    结论:m - nxt[m] 

    nxt[m] 是以m为结尾的字符,与字符串p前缀匹配的最大长度。 

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. char p[100005];
    8. int nxt[100005];
    9. void kmp() {
    10. int m = strlen(p + 1);
    11. int j = 0;
    12. nxt[1] = 0;
    13. for (int i = 2; i <= m; ++i) {
    14. while (j && p[i] != p[j + 1])
    15. j = nxt[j];
    16. if (p[i] == p[j + 1])
    17. ++j;
    18. nxt[i] = j;
    19. }
    20. printf("%d\n", m - nxt[m]);
    21. }
    22. int main(){
    23. scanf("%s", p + 1);
    24. kmp();
    25. return 0;
    26. }

    EXKMP

    拓展kmp,又称Z算法

    模板:

    1. void exkmp() {
    2. int n = strlen(s + 1);
    3. z[1] = 0;
    4. int L = 1, R = 0;
    5. for (int i = 2; i <= n; ++i) {
    6. if (i > R) {
    7. z[i] = 0;
    8. } else {
    9. int k = i - L + 1;
    10. z[i] = min(z[k], R - i + 1);
    11. }
    12. while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]])
    13. ++z[i];
    14. if (i + z[i] - 1 > R) {
    15. L = i, R = i + z[i] - 1;
    16. }
    17. }
    18. }

    z[i]  : 从i开始的字符串与s字符串前缀最大匹配的长度

    字符串匹配

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. const int N = 200005;
    8. char s[N], p[N];
    9. int z[N];
    10. int n, m;
    11. void solve() {
    12. scanf("%s%s", s + 1, p + 1);
    13. n = strlen(s + 1);
    14. m = strlen(p + 1);
    15. p[m + 1] = '#';
    16. for (int i = m + 2, j = 1; j <= n; ++i, ++j) {
    17. p[i] = s[j];
    18. }
    19. z[1] = 0;
    20. int L = 1, R = 0;
    21. for (int i = 2; i <= n + m + 1; ++i) {
    22. if (i > R) {
    23. z[i] = 0;
    24. } else {
    25. int k = i - L + 1;
    26. z[i] = min(z[k], R - i + 1);
    27. }
    28. while (i + z[i] <= n + m + 1 && p[z[i] + 1] == p[i + z[i]])
    29. ++z[i];
    30. if (i + z[i] - 1 > R) {
    31. L = i, R = i + z[i] - 1;
    32. }
    33. }
    34. int ans = 0;
    35. for (int i = m + 2; i <= n + m + 1; ++i) {
    36. if (z[i] == m) ans++;
    37. }
    38. if (ans == 0) puts("-1\n-1");
    39. else {
    40. printf("%d\n", ans);
    41. for (int i = m + 2; i <= n + m + 1; ++i) {
    42. if (z[i] == m) {
    43. printf("%d ", i - m - 1);
    44. }
    45. }
    46. puts("");
    47. }
    48. }
    49. int main(){
    50. int t; scanf("%d", &t);
    51. while (t--) {
    52. solve();
    53. }
    54. return 0;
    55. }

    Secret word 

    1. // problem :
    2. #include
    3. using namespace std;
    4. #define ll long long
    5. typedef pair<int, int> PII;
    6. #define pb push_back
    7. const int N = 1E5 + 5;
    8. char s[N << 1];
    9. int z[N << 1];
    10. void solve() {
    11. int n = strlen(s + 1);
    12. s[n + 1] = '#';
    13. for (int i = n + 2, j = n; j ; --j, ++i) {
    14. s[i] = s[j];
    15. }
    16. z[1] = 0;
    17. int L = 1, R = 0;
    18. for (int i = 2; i <= 2 * n + 1; ++i) {
    19. if (i > R) {
    20. z[i] = 0;
    21. } else {
    22. int k = i - L + 1;
    23. z[i] = min(z[k], R - i + 1);
    24. }
    25. while (i + z[i] <= 2 * n + 1 && s[z[i] + 1] == s[i + z[i]])
    26. ++z[i];
    27. if (i + z[i] - 1 > R) {
    28. L = i, R = i + z[i] - 1;
    29. }
    30. }
    31. int ans = 0;
    32. for (int i = n + 2; i <= 2 * n + 1; ++i) {
    33. ans = max(ans, z[i]);
    34. }
    35. for (int i = ans; i >= 1; --i)
    36. printf("%c", s[i]);
    37. putchar(10);
    38. }
    39. int main(){
    40. scanf("%s", s + 1);
    41. solve();
    42. return 0;
    43. }

    Password 

    1. #include
    2. using namespace std;
    3. #define ll long long
    4. typedef pair<int, int> PII;
    5. #define pb push_back
    6. const int N = 1000005;
    7. char s[N], p[N];
    8. int z[N];
    9. int n, m;
    10. void solve() {
    11. scanf("%s", s + 1);
    12. n = strlen(s + 1);
    13. z[1] = 0;
    14. int L = 1, R = 0;
    15. for (int i = 2; i <= n; ++i) {
    16. if (i > R) {
    17. z[i] = 0;
    18. } else {
    19. int k = i - L + 1;
    20. z[i] = min(z[k], R - i + 1);
    21. }
    22. while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]])
    23. ++z[i];
    24. if (i + z[i] - 1 > R) {
    25. L = i, R = i + z[i] - 1;
    26. }
    27. }
    28. int ans = 0, x = 0;
    29. for (int i = 1; i <= n; ++i) {
    30. if (z[i] == n - i + 1) {
    31. if (x >= z[i])
    32. ans = max(ans, z[i]);
    33. }
    34. x = max(x, z[i]);
    35. }
    36. if (!ans) printf("Just a legend\n");
    37. else {
    38. for (int i = 1; i <= ans; ++i)
    39. printf("%c", s[i]);
    40. puts("");
    41. }
    42. }
    43. int main(){
    44. solve();
    45. return 0;
    46. }

  • 相关阅读:
    100106. 元素和最小的山形三元组 I
    使用protobuf解析Onnx文件
    一文详解 requests 库中 json 参数和 data 参数的用法
    多方合作时,系统间的交互是怎么做的?
    51单片机
    Apache Log4j2漏洞
    【算法leetcode】剑指 Offer II 045. 二叉树最底层最左边的值(rust和go重拳出击)
    品尝葡萄酒要注意的重点事项有哪些?
    加密算法 — — 对token进行非对称加密【RSA】
    文心一言 VS 讯飞星火 VS chatgpt (214)-- 算法导论16.2 1题
  • 原文地址:https://blog.csdn.net/PURSUE__LIFE/article/details/126343013