KMP

KMP

s1 字符串是否包含 s2 字符串,如果包含返回 s1 中包含 s2 的最左开头位置,不包含返回 -1

暴力方法就是 s1 的每个位置都做开头,然后去匹配 s2 整体,时间复杂度 O(n * m)

KMP 算法可以做到时间复杂度 O(n + m)

28. 找出字符串中第一个匹配项的下标

#include <iostream>
#include <vector>

using namespace std;

class Solution {
public:
    // next 数组:规定 next[0] = -1
    // 其他位置 next[i] = a 表示 p[0, i-1] 子串的真前缀和真后缀的最大匹配长度(即前后缀不包含整个子串)为 a
    vector<int> getNextArr(string &p) {
        int m = p.length();
        if (m == 1) return {-1};
        vector<int> next(m);
        // 起始位置规定为 -1
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        // 前面子串中和当前位置进行对比的位置
        int pre = 0;
        while (i < m) {
            if (p[i - 1] == p[pre]) {
                next[i++] = ++pre;
            } else if (pre > 0) {
                // 往前跳
                pre = next[pre];
            } else {
                next[i++] = 0;
            }
        }

        return next;
    }

    // 时间复杂度 O(n + m)
    int kmp(string &s, string &p) {
        int n = s.length();
        int m = p.length();
        // 时间复杂度 O(m)
        vector<int> next = getNextArr(p);

        // 字符串 s 当前匹配位置
        int i = 0;
        // 模式串 p 当前匹配位置
        int j = 0;
        // 时间复杂度 O(n)
        while (i < n && j < m) {
            if (s[i] == p[j]) {
                i++;
                j++;
            } else if (j == 0) {
                i++;
            } else {
                j = next[j];
            }
        }
        return j == m ? i - m : -1;
    }

    int strStr(string haystack, string needle) {
        return kmp(haystack, needle);
    }
};

572. 另一棵树的子树

  • 暴力递归
#include <iostream>
#include <vector>

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
public:
    // 判断两棵树是否完全一样
    bool isSame(TreeNode *r1, TreeNode *r2) {
        if (r1 == nullptr && r2 == nullptr) return true;
        if (r1 == nullptr || r2 == nullptr) return false;
        return r1->val == r2->val && isSame(r1->left, r2->left) && isSame(r1->right, r2->right);
    }

    // 时间复杂度 O(n * m)
    bool isSubtree(TreeNode *root, TreeNode *subRoot) {
        if (root != nullptr && subRoot != nullptr)
            return isSame(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
        return subRoot == nullptr;
    }
};
  • 先序序列化(遇到空节点要记录) + KMP
#include <iostream>
#include <vector>

using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
public:
    // next 数组:规定 next[0] = -1
    // 其他位置 next[i] = a 表示 p[0, i-1] 子串的真前缀和真后缀的最大匹配长度(即前后缀不包含整个子串)为 a
    vector<int> getNextArr(vector<string> &p) {
        int m = p.size();
        if (m == 1) return {-1};
        vector<int> next(m);
        // 起始位置规定为 -1
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        // 前面子串中和当前位置进行对比的位置
        int pre = 0;
        while (i < m) {
            if (p[i - 1] == p[pre]) {
                next[i++] = ++pre;
            } else if (pre > 0) {
                // 往前跳
                pre = next[pre];
            } else {
                next[i++] = 0;
            }
        }

        return next;
    }

    // 时间复杂度 O(n + m)
    int kmp(vector<string> &s, vector<string> &p) {
        int n = s.size();
        int m = p.size();
        // 时间复杂度 O(m)
        vector<int> next = getNextArr(p);

        // 字符串 s 当前匹配位置
        int i = 0;
        // 模式串 p 当前匹配位置
        int j = 0;
        // 时间复杂度 O(n)
        while (i < n && j < m) {
            if (s[i] == p[j]) {
                i++;
                j++;
            } else if (j == 0) {
                i++;
            } else {
                j = next[j];
            }
        }
        return j == m ? i - m : -1;
    }

    // 中序序列化,空节点也记录
    void serial(TreeNode *root, vector<string> &path) {
        if (root == nullptr) {
            path.emplace_back("null");
            return;
        }
        path.emplace_back(to_string(root->val));
        serial(root->left, path);
        serial(root->right, path);
    }

    // 时间复杂度 O(n * m)
    bool isSubtree(TreeNode *root, TreeNode *subRoot) {
        vector<string> s;
        vector<string> p;
        serial(root, s);
        serial(subRoot, p);
        return kmp(s, p) != -1;
    }
};

P4391 [BOI2009] Radio Transmission 无线传输

#include <iostream>
#include <vector>

using namespace std;

vector<int> getNextArr(string s) {
    int n = s.size();
    // 多求了一位
    vector<int> next(n + 1);
    next[0] = -1;
    next[1] = 0;
    int i = 2, pre = 0;
    while (i <= n) {
        if (s[i - 1] == s[pre]) {
            next[i++] = ++pre;
        } else if (pre > 0) {
            pre = next[pre];
        } else {
            next[i++] = 0;
        }
    }
    return next;
}

int main() {
    int n;
    cin >> n;
    string s;
    cin >> s;

    vector<int> next = getNextArr(s);
    cout << n - next[n];
}

P4824 [USACO15FEB] Censoring S

#include <iostream>
#include <vector>
#include <stack>

using namespace std;

int MAXN = 1000001;
vector<int> nxt(1000001);
// <s 中的位置 i,t 中与 s[i] 匹配的位置>
vector<pair<int, int>> stk(MAXN);
// 栈顶位置
int top;

void generateNxt(string s) {
    nxt[0] = -1;
    int n = s.size();
    if (n == 1) return;
    nxt[1] = 0;
    int i = 2, pre = 0;
    while (i < n) {
        if (s[i - 1] == s[pre]) {
            nxt[i++] = ++pre;
        } else if (pre > 0) {
            pre = nxt[pre];
        } else {
            nxt[i++] = 0;
        }
    }
}

int main() {
    string s, t;
    cin >> s >> t;
    int n = s.length();
    int m = t.length();

    top = 0;
    generateNxt(t);

    int i = 0, j = 0;
    while (i < n) {
        if (s[i] == t[j]) {
            stk[top++] = make_pair(i, j);
            i++;
            j++;
        } else if (j == 0) {
            stk[top++] = make_pair(i, -1);
            i++;
        } else {
            j = nxt[j];
        }
        if (j == m) {
            // 弹出一个字符串 t
            top -= m;
            // 如果栈非空,栈顶 s[stk[top - 1].first] 当前和 t[stk[top - 1].second] 匹配,就从下一位置继续和 i 对比
            // 如果栈空,就从 t 的起始位置继续和 i 对比
            j = top > 0 ? stk[top - 1].second + 1 : 0;
        }
    }

    string res = "";
    // 输出栈中剩下的字符
    for (int k = 0; k < top; ++k)
        res += s[stk[k].first];
    cout << res;
}

1367. 二叉树中的链表

  • 递归
using namespace std;

struct ListNode {
    int val;
    ListNode *next;

    ListNode() : val(0), next(nullptr) {}

    ListNode(int x) : val(x), next(nullptr) {}

    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
public:
    // 判断是否能从当前节点匹配全部链表
    bool dfsJudge(struct TreeNode *root, struct ListNode *head) {
        if (head == nullptr) return true;
        if (root == nullptr || root->val != head->val) return false;
        return dfsJudge(root->left, head->next) || dfsJudge(root->right, head->next);
    }

    // 遍历二叉树
    bool dfs(struct TreeNode *root, struct ListNode *head) {
        if (root == nullptr) return false;
        if (dfsJudge(root, head)) return true;
        return dfs(root->left, head) || dfs(root->right, head);
    }

    bool isSubPath(ListNode *head, TreeNode *root) {
        return dfs(root, head);
    }
};
  • KMP
#include <iostream>
#include <vector>

using namespace std;

struct ListNode {
    int val;
    ListNode *next;

    ListNode() : val(0), next(nullptr) {}

    ListNode(int x) : val(x), next(nullptr) {}

    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
public:

    void generateNext(ListNode *head, vector<int> &list, vector<int> &next) {
        // 链表转成数组
        while (head != nullptr) {
            list.emplace_back(head->val);
            head = head->next;
        }
        int n = list.size();
        // 生成 list 数组的 next 数组
        next.resize(n);
        next[0] = -1;
        if (n == 1) return;
        next[1] = 0;
        int i = 2, pre = 0;
        while (i < n) {
            if (list[i - 1] == list[pre]) {
                next[i++] = ++pre;
            } else if (pre > 0) {
                pre = next[pre];
            } else {
                next[i++] = 0;
            }
        }
    }

    // 从 list[cur] 和 root.val 开始往下对比,返回能否将 list 全部匹配
    bool dfs(vector<int> &list, vector<int> &next, TreeNode *root, int cur) {
        if (cur == list.size()) return true;
        if (root == nullptr) return false;
        // 失配时,找到上一个匹配的位置
        // 均摊下来,时间复杂度 O(1)
        while (cur >= 0 && root->val != list[cur])
            cur = next[cur];
        // 推出循环时,i == -1 或者 i >=0 (匹配上了)
        // 子树从下个位置 list[cur + 1] 对比
        return dfs(list, next, root->left, cur + 1) || dfs(list, next, root->right, cur + 1);
    }

    // 时间复杂度 O(n + m)
    bool isSubPath(ListNode *head, TreeNode *root) {
        vector<int> list;
        vector<int> next;
        generateNext(head, list, next);
        return dfs(list, next, root, 0);
    }
};

1397. 找到所有好字符串

// todo 数位DP
posted @ 2024-10-19 10:38  n1ce2cv  阅读(8)  评论(0编辑  收藏  举报