[补档计划] 字符串
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[补档计划] 字符串相关的知识,希望对你有一定的参考价值。
学习一个算法, 需要弄清一些地方:
① 问题与算法的概念;
② 算法, 思维轨迹
③ 实现, 思维轨迹;
④ 复杂度分析;
⑤ 应用.
KMP算法
字符串匹配与KMP算法
为了方便弄清问题, 应该从特例入手.
设 A = " ababababb " , B = " ababa " , 我们要研究下面三个递进层次的字符串匹配问题:
① 是否存在 A 的子串等于 B
② 有几个 A 的子串等于 B
③ A 的哪些位置的子串等于 B
KMP算法可以直接在线性复杂度解决问题③, 进而更可以解决问题①和问题②.
KMP算法
定义 next 数组, 沿着 next 往前跳, 直到匹配.
next 数组的求法与匹配的过程相同.
KMP算法的实现
#define F(i,a,b) for (register int i=(a);i<=(b);i++) int nS; char s[S]; int nx[S]; int nT; char t[T]; bool KMP(void) { for (int i=2,j=0;i<=nS;i++) { while (j<=nS && s[i]!=s[j+1]) j=nx[j]; nx[i] = (s[i]==s[j+1] ? ++j : j); } for (int i=1,j=0;i<=nT;i++) { while (j<=nT && t[i]!=s[j+1]) j=nx[j]; if (s[i]==s[j+1]) { j++; if (j==nT) return true; } } return false; }
KMP算法的复杂度分析
空间复杂度: O(n) .
时间复杂度: O(n) .
由于每次 j 只会往后移一位, 所以往后移的总位数为 O(n) .
每次 j 往前跳, 至少会跳一位, 所以往前跳的总次数为 O(n) .
综上, 均摊时间复杂度为 O(n) .
[XSY2034] [POI2010] Hamster
题意 给定 n 种字符串 {Si} . 求最短的字符串, 含有 m 个 {Si} .
m <= 10^9, n <= 200, ∑|s| <= 10^6
分析 KMP + 倍增Floyd.
两两 KMP 求增加长度, 倍增 Floyd.
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; #define F(i, a, b) for (register int i = (a); i <= (b); i++) #define D(i, a, b) for (register int i = (a); i >= (b); i--) #define LL long long const int N = 205; const int L = 100005; const LL INF = ~0ull>>2; int n, m, start[N], len[N], tot; char s[L]; struct Info { LL go[N][N]; friend Info operator + (Info &A, Info &B) { Info C; F(i, 1, n) F(j, 1, n) C.go[i][j] = INF; F(k, 1, n) F(i, 1, n) F(j, 1, n) C.go[i][j] = min(C.go[i][j], A.go[i][k] + B.go[k][j]); return C; } LL ans(void) { LL tiny = INF; F(i, 1, n) F(j, 1, n) tiny = min(tiny, go[i][j]); return tiny; } }res, c; int calc(char *A, int nA, char *B, int nB, bool tag) { static int nx[L]; for (int i = 2, j = 0; i <= nB; i++) { while (j > 0 && B[i] != B[j+1]) j = nx[j]; nx[i] = (B[i] == B[j+1] ? ++j : j); } if (tag) return nB-nx[nB]; for (int i = 1, j = 0; i <= nA; i++) { while (j > 0 && A[i] != B[j+1]) j = nx[j]; j += (A[i] == B[j+1]); if (i == nA) return nB-j; } } int main(void) { #ifndef ONLINE_JUDGE freopen("xsy2034.in", "r", stdin); freopen("xsy2034.out", "w", stdout); #endif static char t[L]; scanf("%d%d", &n, &m); m--; F(i, 1, n) { scanf("%s", t+1); start[i] = tot+1; len[i] = strlen(t+1); F(j, 1, len[i]) s[++tot] = t[j]; } F(i, 1, n) F(j, 1, n) res.go[i][j] = (i == j ? len[i] : INF); F(i, 1, n) F(j, 1, n) c.go[i][j] = calc(s+start[i]-1, len[i], s+start[j]-1, len[j], i == j); for (; m > 0; m >>= 1) { if (m%2 == 1) res = res+c; c = c+c; } printf("%lld\\n", res.ans()); return 0; }
以上是关于[补档计划] 字符串的主要内容,如果未能解决你的问题,请参考以下文章