strlen(s) O(n)
Trie树
O(sigma len[i]);
AC自动机
建立一个trie树
fail指针:根的fail指针指向自己,其余节点找到一个最长后缀使得它是原来某个串的一个前缀,fail指针指向该前缀在trie树上的位置
fail指针性质:1.指向比它浅的节点(除根的fail指针外)
2.所有的fail指针组成的是一棵树fail树(除根的fail指针外:n个点,n-1条边,无环)
匹配:按位匹配,若不能向下匹配则沿fail指针向上跳,直到可向下匹配或走到根
O(M) (原因:由于每匹配一位最多向下跳一次,共最多向下跳M次,故最多向上跳M次)
一种用来理解的代码:
inline void build_AC(){//bfs queue<int>q; q.push(rt); while(!q.empty()){ int x=q.front();q.pop(); for(int a=0;a<size;a++){//size 有多少个儿子 if(z[now].next[a]){ //now这个节点有没有a这个儿子 int p=z[now].fail; while(p){ if(z[p].next[a]){ z[z[now].next[a]].fail=z[p].next[a]; break; } p=z[p].fail; } if(!p) z[z[now].next[a]].fail=rt; q.push(z[now].next[a]); }else{//补齐:匹配时不用分类向上跳,直接沿着next走就好了 z[now].next[a]=z[z[now].fail].next[a]; if(!z[now].next[a]) z[now].next[a]=rt; } } } }
后缀数组
把n个后缀排序
rankk[i]表示从每个位置开始排k个字符,i排在第几名
rank2[i] - > rank4[i] - > rank8[i] - > …… - > rank 2^k [i]
基数排序:O(2n)
struct node{ int pos,x[2];//pos开始的位置是哪里,x[0]与x[1]存储用来基数排序的两个值 node(){} node(int a,int b,int c){ pos=a;x[1]=b;x[0]=c; } }x[maxn],sw[maxn]; inline void count_sort(int upper){ for(int a=0;a<=1;a++){ memset(buf,0,sizeof(buf)); for(int b=1;b<=n;b++) buf[x[b].x[a]]++; for(int b=1;b<=upper;b++) inline void sa(){ for(int a=1;a<=n;a++) x[a]=node(a,z[a],0); count_sort(n);//得到所有rank1[i] for(int a=1;a<=n;a<<=1){ for(int b=1;b<=n;b++) x[b]=node(b,rank[b],a+b<=n?rank[a+b]:0); count_sort(n); }//rank[i]第i个后缀排名第几 for(int a=1;a<=n;a++) sa[rank[a]]=a;//sa[i]排名为i的后缀是第几个后缀 }
height数组
height[i]表示排名为i的后缀和排名为i-1的后缀的最长公共前缀长度
应用:求任意两个后缀的最长公共前缀 - > height数组区间最小值
Manacher算法--对称
()()不是回文串23333~~
求以每一个位置为中心的最长回文串长度,均为奇数
在中间插字符,得到偶数长度回文串
https://www.luogu.org/problemnew/show/P3805
https://www.luogu.org/problemnew/show/P4287
Z-Box(扩展KMP算法)--平移
求每一个后缀与原串的最长公共前缀
f[1]=l
f[2]=
求f[i],设f[1]---f[i-1]已经求完了,找到右端点最靠右的一串,设起点为p,终点为r
f[i]=min(r-i+1,f[i-p+1])
Hash
自然溢出:mod 2^64 unsigned long long
单模:1个10^9左右的质数(容易被卡)
双模:2个10^9左右的质数(会被卡常)
DFA
确定状态自动机--有向图,一个起始点,多少个终结点??
从一个点出发的边不能有字母相同的
NFA
不确定状态自动机--有向图,一个起始点,任意多个终结点
从一个点出发的边可以有字母相同的