字符串

KMP

// 替换掉所有word中的p
// Created by Jiang Yinzuo on 2020/3/22.
//

#include <cstdio>
#include <cstring>

const int MAX= 5000007;
int next[MAX];

void get_next(char *word, int len) {
    next[0] = -1;
    int i = 0, j = -1;
    while (i < len) {
        if (j != -1 && word[i] != word[j]) {
            j = next[j];
        } else {
            next[++i] = ++j;
        }
    }
}

int pos[MAX];
char p[MAX], word[MAX];
char ans[MAX];

int main() {

    int ans_idx;
    while (~scanf("%s%s", word, p)) {
        int len = strlen(word);
        get_next(word, len);

        int p_len = strlen(p);
        int i = 0, j = 0;
        ans_idx = 0;
        while (i < p_len) {
            ans[ans_idx] = p[i];
            while (j != -1 && p[i] != word[j]) {
                j = next[j];
            }
            ++i;
            ++j;
            pos[ans_idx++] = j;
            if (j == len) {
                ans_idx -= len;
                j = pos[ans_idx - 1];
            }
        }
        ans[ans_idx] = '\0';
        printf("%s\n", ans);
    }
    return 0;
}

利用KMP算法中的get_next()可以求字符串最大回文前缀(cf1326D)

字符串哈希

//
// Created by jiang on 2020/8/14.
// POJ3461 子串匹配

#include <cstdio>
#include <cstring>

unsigned char s[1000002];
unsigned char word[10002];

unsigned long hash_table[1000002];
unsigned long p[1000002];

void pre_hash(const unsigned char *str) {
    hash_table[0] = 0;
    p[0] = 1;
    for (int i = 1; str[i - 1]; ++i) {
        hash_table[i] = hash_table[i - 1] * 31 + str[i - 1];
        p[i] = p[i - 1] * 31;
    }
}

/**
 * 获取子串哈希值
 * @param l
 * @param r
 * @return
 */
inline unsigned long get_hash(int l, int r) {
    return hash_table[r] - hash_table[l - 1] * p[r - l + 1];
}

/**
 * BKDR hash函数
 * @param str
 * @return hashcode
 */
unsigned long hashcode(unsigned char *str) {
    unsigned long hash_value = 0;
    for (; *str; ++str) {
        hash_value = hash_value * 31 + *str;
    }
    return hash_value;
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {

        scanf("%s %s", word, s);
        pre_hash(s);

        unsigned long hash_value = hashcode(word);
        int count = 0;
        int n = strlen(reinterpret_cast<const char *>(s));
        int len = strlen(reinterpret_cast<const char *>(word));
        for (int i = 1; i <= n - len + 1; ++i) {
            if (hash_value == get_hash(i, i + len - 1)) {
                ++count;
            }
        }

        printf("%d\n", count);
    }
    return 0;
}

AC自动机

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>

using namespace std;
struct Tree//字典树 
{
    int fail;//失配指针
    int vis[26];//子节点的位置
    int end;//标记以这个节点结尾的单词编号
} AC[100000];//Trie树
int cnt = 0;//Trie的指针
struct Result {
    int num;
    int pos;

    bool operator<(const Result &b) const {
        if (num != b.num)
            return num > b.num;
        else
            return pos < b.pos;
    }
} Ans[100000];//所有单词的出现次数

string text[100000];

inline void Clean(int x) {
    memset(AC[x].vis, 0, sizeof(AC[x].vis));
    AC[x].fail = 0;
    AC[x].end = 0;
}

void Build(const string &s, int Num) {
    int now = 0;//字典树的当前指针
    for (int i = 0; i < s.length(); ++i)//构造Trie树
    {
        if (AC[now].vis[s[i] - 'a'] == 0)//Trie树没有这个子节点
        {
            AC[now].vis[s[i] - 'a'] = ++cnt;//构造出来
            Clean(cnt);
        }
        now = AC[now].vis[s[i] - 'a'];//向下构造
    }
    AC[now].end = Num;//标记单词结尾
}

//构造fail指针
void get_fail() {
    queue<int> q;//队列
    for (int i = 0; i < 26; ++i)//第二层的fail指针提前处理一下
    {
        if (AC[0].vis[i] != 0) {
            AC[AC[0].vis[i]].fail = 0;//指向根节点
            q.push(AC[0].vis[i]);//压入队列
        }
    }
    //BFS求fail指针
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < 26; ++i)//枚举所有子节点
        {
            if (AC[u].vis[i] != 0)//存在这个子节点
            {
                AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
                //子节点的fail指针指向当前节点的fail指针所指向的节点的相同子节点
                q.push(AC[u].vis[i]);//压入队列
            } else//不存在这个子节点
                AC[u].vis[i] = AC[AC[u].fail].vis[i];
            //当前节点的这个子节点指向当前节点fail指针的这个子节点
        }
    }
}

int AC_Query(const string &s)//AC自动机匹配
{
    int l = s.length();
    int now = 0, ans = 0;
    for (int i = 0; i < l; ++i) {
        now = AC[now].vis[s[i] - 'a'];//向下一层
        for (int t = now; t; t = AC[t].fail)//循环求解
            Ans[AC[t].end].num++;
    }
    return ans;
}

int main() {
    int n;
    while (true) {
        cin >> n;
        if (n == 0)break;
        cnt = 0;
        Clean(0);
        for (int i = 1; i <= n; ++i) {
            cin >> text[i];
            Ans[i].num = 0;
            Ans[i].pos = i;
            Build(text[i], i);
        }
        AC[0].fail = 0;//结束标志
        get_fail();//求出失配指针
        cin >> text[0];//文本串
        AC_Query(text[0]);
        sort(&Ans[1], &Ans[n + 1]);
        cout << Ans[1].num << endl;
        cout << text[Ans[1].pos] << endl;
        for (int i = 2; i <= n; ++i) {
            if (Ans[i].num == Ans[i - 1].num)
                cout << text[Ans[i].pos] << endl;
            else
                break;
        }
    }
    return 0;
}

字典树(Trie树)

模板

https://www.acwing.com/problem/content/144/

https://ac.nowcoder.com/acm/contest/4370/B 2019上海区域赛B

// 总共几个字符(连续的),起始字符,输入文本总共有多少字符
template<const int NumCh, const char BaseCh, const int TextTotLen>
struct Trie {
protected:
    int next[TextTotLen][NumCh], idx;
    bool exist[TextTotLen];  // 该结点结尾的字符串是否存在

public:
    /**
     * 插入字符串
     * @param s 待插入的字符串
     */
    void insert(char *s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!next[p][c])
                next[p][c] = ++idx;  // 如果没有,就添加结点
            p = next[p][c];
        }
        exist[p] = true;
    }

    /**
     * 查找字符串
     * @param s 待查找的字符串
     * @return 是否存在
     */
    bool find(char *s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!next[p][c])
                return false;
            p = next[p][c];
        }
        return exist[p];
    }
};

template<const int NumCh, const char BaseCh, const int TextTotLen>
struct FindPrefixTrie : public Trie<NumCh, BaseCh, TextTotLen> {
protected:
    int visited_cnt[TextTotLen]{0};
    int existed_cnt[TextTotLen]{0};
public:
    /**
     * 插入字符串
     * @param s 待插入的字符串
     */
    void insert(char *s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!this->next[p][c])
                this->next[p][c] = ++this->idx;  // 如果没有,就添加结点
            p = this->next[p][c];
        }
        ++existed_cnt[p];
    }

    /**
     * 插入时检查s和Trie中的字符串是否存在前缀关系
     * @return True: 没有前缀;false:s是Trie中的字符串的前缀,或Trie中的字符串是s的前缀
     */
    bool insert_no_prefix(char *s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!this->next[p][c])
                this->next[p][c] = ++this->idx;
            p = this->next[p][c];
            if (this->exist[p]) {
                return false;  // Trie中某个字符串是s的前缀
            }
            ++visited_cnt[p];
        }
        this->exist[p] = true;
        return this->visited_cnt[p] <= 1;  // 若为false,代表s是Trie中某字符串的前缀
    }

    /**
     * 查找Trie中是否存在字符串s的前缀
     * @param s 待查找的字符串
     * @return 是否存在
     */
    bool find_prefix(char *s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!this->next[p][c])
                return false;
            p = this->next[p][c];
            if (this->exist[p])
                return true;
        }
        return this->exist[p];
    }

    int prefix_cnt(char *s) {
        int res = 0, p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BaseCh;
            if (!this->next[p][c])
                return res;
            p = this->next[p][c];
            res += this->existed_cnt[p];
        }
        return res;
    }
};