声明
本人在理解kuangbin的模板之后写下注释,如有错请谅解,可留言告诉我错误谢谢;
附上kuangbin的一些网站:
kuangbin的ACM模板:https://kuangbin.github.io/2018/08/01/ACM-template/
kuangbin的博客:https://kuangbin.github.io
kuangbin的博客园:https://www.cnblogs.com/kuangbin/
kuangbin的GitHub:https://github.com/kuangbin
1、字符串处理
1.1、KMP
#include<stdio.h>#include<string.h>/** man[] 主串* pat[] 模式串* next[i] 失配数组,数组值为模式串前 i 项构成的子串前缀后缀相同部分长度的最大值** kmpPre(pat[],patlen,next[])* 以 i 和 j 为指针,模式串跟自己进行匹配,构建失配数组以方便模式串在主串中配对时进行跳转* kmpCount(pat[],patlen,man[],manlen)* 以 i 和 j 为指针,模式串在失配数组的辅助下在主串中进行匹配,输出模式串在主串中出现的次数 ans*/int next[10010];void kmpPre(char pat[], int patLen, int next[]){int i,j;i=0; j=next[0]=-1;while(i<patLen){while(j!=-1 && pat[i]!=pat[j]) j=next[j];i++; j++;next[i]=j;}}int kmpCount(char pat[], int patLen, char man[], int manLen){int i,j;int ans=0;kmpPre(pat,patLen,next);i=j=0;while(i<manLen){while(j!=-1 && man[i]!=pat[j]) j=next[j];i++; j++;if(j>=patLen){ans++;j=next[j];}}return ans;}int main(){char man[]={"ababababca"};char pat[]= {"abababca"};int ans=kmpCount(pat,strlen(pat),man,strlen(man));printf("%d\n", ans);return 0;}
1.3、manacher
#include<cstdio>#include<cstring>#include<iostream>using namespace std;/** MAXN 原字符串长度* str[] 原字符串* ma[] 加工串,填充处理后的字符串* mp[i] 以 ma[] 为模板,当前位置 i 为子回文串中心,值为该回文串半径长度* '$' 开头占位符* '#' 字符间占位符* mx 代表当前<已经匹配完毕的结尾最远的回文串>到达了ma[]数组的第mx位* id 代表当前<已经匹配完毕的结尾最远的回文串>中心为ma[]数组的第id位** Mp[i]=1,说明Mx没有覆盖超过i,那么Mx的値在这一步执行后一定会增加* Mp[i]=Mx-i,说明以Ma[i]为中心的回文串至少到达了Mx,那么Mx的値在这之后不会减少* Mp[i]=Mp[ID*2-i],说明Ma[i]已经匹配不下去了** str[i]: a b a a b a* i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14* ma[i]: $ # a # b # a # a # b # a # /0* mp[i]: 1 1 2 1 4 1 2 7 2 1 4 1 2 1*/const int MAXN=100;char str[MAXN], ma[MAXN*2];int mp[MAXN*2];void manacher(int sLen){int maCount=0;ma[maCount++]='$';ma[maCount++]='#';for(int i=0; i<sLen; i++){ma[maCount++]=str[i];ma[maCount++]='#';}ma[maCount]=0;int mx=0;int id=0;for(int i=0; i<maCount; i++){mp[i]=mx>i?min(mp[2*id-i],mx-i):1;while(ma[i+mp[i]]==ma[i-mp[i]])mp[i]++;if(i+mp[i]>mx){mx=i+mp[i];id=i;}}}int main(){while (scanf("%s", str) == 1){int sLen=strlen(str);manacher(sLen);int ans=0;for (int i=0; i<2*sLen+2; i++)ans=max(ans,mp[i]-1);printf("%d\n", ans);}return 0;}
1.4、AC自动机
#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>#include <queue>using namespace std;/** MAXN 字典树节点序号总数* root 根节点序号,一直为0* L 为当前节点序号* buf[] 临时存储字符串(模式串和文章)* next[i][j]* 节点连接数组,i 为字典树当前节点序号,j 为该节点字母序号,数组值为相对应下一个字典树节点的序号(靠左节点优先赋值)* fail[i]* 失配数组,i 为字典树当前节点序号,数组值为当前节点相对应下一个字典树节点失配后跳转到的字典树节点序号{a->a)* end[i]* 单词尾部加数,字典树当前节点序号为 i,数组值为以当前节点为结尾的单词数(大多数时候为1)** init() 初始化字典树建立根* newnode() 建立新节点* insert(buf[]) now 为当前节点序号,向字典树中装入模式串* build() 用队列 Q 依次存节点序号,构建失配数组方便查询* query(buf[]) 输入文章,输出 n 个模式串在文章中出现的个数(?/n个)*/const int MAXN=500010;struct Trie{int next[MAXN][26], fail[MAXN], end[MAXN];int root, L;void init(){L = 0;root = newnode();}int newnode(){for (int i = 0; i < 26; i++)next[L][i] = -1;end[L++] = 0;return L - 1;}void insert(char buf[]){int len = strlen(buf);int now = root;for (int i = 0; i < len; i++){if (next[now][buf[i] - 'a'] == -1)next[now][buf[i] - 'a'] = newnode();now = next[now][buf[i] - 'a'];}end[now]++;}void build(){queue<int> Q;fail[root] = root;for (int i = 0; i < 26; i++)if (next[root][i] == -1)next[root][i] = root;else{fail[next[root][i]] = root;Q.push(next[root][i]);}while (!Q.empty()){int now = Q.front();Q.pop();for (int i = 0; i < 26; i++)if (next[now][i] == -1)next[now][i] = next[fail[now]][i];else{fail[next[now][i]] = next[fail[now]][i];Q.push(next[now][i]);}}}int query(char buf[]){int len = strlen(buf);int now = root;int res = 0;for (int i = 0; i < len; i++){now = next[now][buf[i] - 'a'];int temp = now;while (temp != root){res += end[temp];end[temp] = 0;temp = fail[temp];}}return res;}void debug(){for (int i = 0; i < L; i++){printf("id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i]);for (int j = 0; j < 26; j++)printf("%2d", next[i][j]);printf("]\n");}}};char buf[1000010];Trie ac;int main(){int T;int n;scanf("%d", &T);while (T--){scanf("%d", &n);ac.init();for (int i = 0; i < n; i++){scanf("%s", buf);ac.insert(buf);}ac.build();scanf("%s", buf);printf("%d\n", ac.query(buf));}return 0;}
2、数学
2.1、素数
2.1.1 素数筛选(判断 <MAXN 的数是否素数)
/** MAXN 判断是否为素数的最大值* notprime[i] i 为所指数字,其值为 false 表示为素数,true 表示不为素数** 素数:指在大于1的自然数中,只有1和它本身能整除的数* 存储小于 MAXN 的数是不是素数这一状态*/#include<cstring>const int MAXN = 1000010;bool notprime[MAXN];void init(){memset(notprime, false, sizeof(notprime));notprime[0] = notprime[1] = true;for (int i = 2; i < MAXN; i++)if (!notprime[i]){//防止后面 i*i 溢出 (或者 i,j 用 long long)if (i > MAXN / i)continue;//直接从 i*i 开始就可以,小于 i 倍的已经筛选过了, 注意是 j+=ifor (int j = i * i; j < MAXN; j += i)notprime[j] = true;}}
