等价变形为 \(x_i\leq x_j+c_k\),与最短路的松弛条件很相似,所以可以从 \(j\) 向 \(i\) 建一条 \(c_k\) 的边,再新建 \(0\) 号点向所有点连边,边权为 \(0\),从 \(0\) 号点开始跑单源最短路,如果有负环证明无解。
注:判负环应使用 Bellman-Ford 或 队列优化的 Bellman-Ford (某已死算法)。
求解欧拉回路。
条件:已经判断其存在欧拉回路。
选择任意顶点为起点,进行 DFS,并删掉所扩展的边,如果搜到一个点不能继续扩展,就把这个点放在最后面,这样就倒序模拟出了从起点开始的方案。
原理:不断寻找增广路径,并通过递归实现 “撤销和更换” 操作。
对于任何左部点的子集 \(S\) 都要满足 \(\left|S\right|\leq\left|T\right|\),其中 \(T\) 表示 \(S\) 中的点能到达右部点的并。
std::map
对于一个长度为 \(l\) 的字符串 \(s\) 而言,我们定义多项式 \(f_s=\sum_{i=1}^m s_i\times b^{l-i} \pmod P\) 为字符串 \(s\) 的 Hash 函数,实际上也可以将其理解为字符串 \(s\) 在 \(b\) 进制下的结果。
ull
自然溢出,如果字符串长度过大可考虑使用双 Hash。无法使用 KMP,但是可以 Hash + 二分。
枚举所有可能匹配的子串,假设现在枚举的子串为 \(s‘\),通过 Hash + 二分可以快速找到 \(s‘\) 和 \(p\) 第一个不同的位置。之后将 \(s‘\) 和 \(p\) 在这个失配位置以及之前的部分删掉,继续查找下一个失配位置,这样的过程最多发生 \(k\) 次。
时间复杂度:\(O(m+kn\log m)\)
主要用来在源串中查找模式串的出现的次数和位置。
核心思想:减少不必要的匹配以加快匹配。
\(\rm nxt_i\) 表示以 \(i\) 为结尾的前缀中,最长相等前后缀的长度。
\(\rm nxt_i\) 求法:
for(int i=2,j=0;i<=m;i++){
while(j&&p[j+1]!=p[i])
j=nxt[j];
if(p[j+1]==p[i])
j++;
nxt[i]=j;
}
匹配做法:
for(int i=1,j=0;i<=n;i++){
while(j&&p[j+1]!=s[i])
j=nxt[j];
if(p[j+1]==s[i])
j++;
if(j==m){
ans[++cnt]=i-m+1;
j=nxt[j];
}
}
int next[N][26],cnt;
int num[N];
//插入
void insert(char *s,int l){
int p=0;
for(int i=0;i<l;i++){
int ch=s[i]-‘a‘;
if(!next[p][c]) next[p][c]=++cnt;
p=next[p][c];
}
num[p]++;
}
//查询
int find(char *s,int l){
int p=0;
for(int i=0;i<l;i++){
int ch=s[i]-‘a‘;
if(!next[p][c]) return 0;
p=next[p][c];
}
return num[p];
}
可以进行多模式串匹配。
以 Trie 树的结构为基础,结合 KMP 的思想建立的。
\(\rm Fail\) 求法:
void build(){
for(int i=0;i<26;i++)
if(tr[0][i]) q.push(tr[0][i]);
while(q.size()){
int u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
q.push(tr[u][i]);
}else tr[u][i]=tr[fail[u]][i];
}
}
}
多模式匹配:
int query(char *t){
int u=0,res=0;
for(int i=1;t[i];i++){
u=tr[u][t[i]-‘a‘];
for(int j=u;j&&e[j]!=-1;j=fail[j]){
res+=e[j];e[j]=-1;
}
}
return res;
}
时间复杂度:\(O(26n+m)\),其中 \(n\) 为节点数,\(m\) 为源串长度。
原文:https://www.cnblogs.com/blueqwq/p/15023798.html