KMP算法題記
照著這篇博客刷一下。 自己也做一下筆記
poj 3461 Oulipo
基於兩個串a和b,問a在b中重復了幾次。要對KMP進行一些修改,其實只是在模式串匹配完之後,ans++,並且讓模式串的j回到原來的位置重來而已。
#include <cstdio> #include <cstring> using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #define maxN 1000005 int nxt[maxN], n, m, cas, ans; char a[maxN], b[maxN];void getNxt(char *b, int m, int *nxt) { nxt[0] = -1; int i = 0, j = -1; while (i < m) { if (j == -1 || b[i] == b[j]) ++i, ++j, nxt[i] = j; else j = nxt[j]; } } void kmp(char *a, int n, char *b, int m) { getNxt(b, m, nxt); int i = 0, j = 0; while (i < n) { while(-1 != j && a[i] != b[j]) j = nxt[j]; ++i, ++j; if (j >= m) { ++ans; j = nxt[j]; } } } int main () { // freopen("data.in", "r", stdin); scanf("%d", &cas); while (cas--) { scanf("%s%s", b, a); n = (int)strlen(a), m = (int)strlen(b); ans= 0; kmp(a, n, b, m); printf("%d\n", ans); } return 0; }
poj 2752 Seek the Name, Seek the Fame
給你一個字符串,問前綴和後綴相同的字符串長度可以為多少?
考的是對next數組的理解。假設串為s,長度為L,那麽next[L],即是s的最長前後綴長度,是答案之一,這裏設這裏的最長前綴為A,最長後綴為B。更短的”前綴-後綴串“必然也是A的前綴和B的後綴的公共部分,又因為A=B,那麽問題變成了A的最長公共前後綴問題,如此便可不斷回溯回去。
#include <cstdio> #include <cstring> using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #define maxN 400005 char a[maxN]; int m, ans[maxN], nxt[maxN]; void getNxt(char *b, int m, int *nxt) { nxt[0] = -1; int i = 0, j = -1; while (i < m) { if (j == -1 || b[i] == b[j]) ++i, ++j, nxt[i] = j; else j = nxt[j]; } } int main () { // freopen("data.in", "r", stdin); while (~scanf("%s", a)) { m = (int)strlen(a); getNxt(a, m, nxt); int cnt = 0; int cur = m, j = nxt[cur]; while (j) ans[++cnt] = j, j = nxt[j]; FOR(i, 1, cnt) printf("%d ", ans[cnt - i + 1]); printf("%d\n", m); } return 0; }
poj 2406 Power Strings
問的是一個字符串,其最多能由多少個循環節構成。可以參考這篇說明
#include <cstdio> #include <cstring> using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #define maxN 1000005 char s[maxN]; int nxt[maxN]; void getNxt(char *b, int m, int *nxt) { nxt[0] = -1; int i = 0, j = -1; while (i < m) { if (j == -1 || b[i] == b[j]) nxt[++i] = ++j; else j = nxt[j]; } } int main () { // freopen("data.in", "r", stdin); while (~scanf("%s", s) && strcmp(s, ".")) { int m = (int)strlen(s); getNxt(s, m, nxt); if (m % (m - nxt[m]) == 0) printf("%d\n", m / (m - nxt[m])); else puts("1"); } return 0; }
hdu 3746 Cyclic Nacklace
問的是最少加入幾個字符能使得這個串是循環的。
分幾種情況:1,整個串無法被循環, 即nxt[m]=0,此時直接再來一個串接後面才行。
2,本身已經是循環串,此時m%(m-nxt[m])==0,直接輸出0即可。
3,前綴是循環串,這個時候找到那個nxt[i]==0 (意味著前面就一個串,不循環),或者是i%(i-nxt[i])==0,此時即[0,i)這個串是循環串,得出最小循環節長度,設為L,答案就是L-後綴長度。
#include <cstdio> #include <cstring> using namespace std; #define maxN 100006 int nxt[maxN], cas, idx; char a[maxN]; void getNxt(char *v, int m) { nxt[0] = -1; int i = 0, j = -1; while (i < m) { if (j == -1 || v[i] == v[j]) nxt[++i] = ++j; else j = nxt[j]; if (nxt[i] == 0 || i % (i - nxt[i]) == 0) idx = i; } } int main () { // freopen("data.in", "r", stdin); scanf("%d", &cas); while (cas--) { scanf("%s", a); int m = (int)strlen(a); getNxt(a, m); if (nxt[m] == 0) { printf("%d\n", m); } else if (m % (m - nxt[m]) == 0) { puts("0"); } else { // 循環節長度 int L = idx - nxt[idx]; int tail = m - idx; printf("%d\n", L - tail); } } return 0; }
hdu 3336 Count the string
問的是所有的前綴,在字符串中一共出現了幾次?
假設某個前綴A和後綴B一樣,那麽B相當於給A貢獻了B.length()分數。於是乎問題變成了:問有多少和前綴串相同的後綴串。但是因為如果單反前後綴一樣就加分,會重復計算,比如說:
aaauvwaaa,第7和第8個a組成的aa會貢獻2分,當加入第9個a成為aaa時,如果你又認為貢獻3分,就會重復計算了第7個第8個連成的"aa"的分數。於是:當nxt[i]+1!=nxt[i+1]時,表示到i的後綴和到i+1的後綴是不一樣的,才進行加分。
#include <cstdio> using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i) #define maxN 200005 int nxt[maxN], cas, n; char a[maxN]; void getNxt(char *v, int m) { nxt[0] = -1; int i = 0, j = -1; while (i < m) { if (j == -1 || v[i] == v[j]) nxt[++i] = ++j; else j = nxt[j]; } } int main () { // freopen("data.in", "r", stdin); scanf("%d", &cas); while (cas--) { scanf("%d %s", &n, a); getNxt(a, n); int ans = (n + nxt[n]) % 10007; FOR(i, 0, n - 1) { if (nxt[i] && nxt[i] + 1 != nxt[i + 1]) ans = (ans + nxt[i]) % 10007; } printf("%d\n", ans); } return 0; }
KMP算法題記