「刷题记录」LOJ/一本通提高篇 KMP算法
「剪花布条」
题目传送门:剪花布条
思路:模板题要什么思路
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10000010;
int m1, m2;
char s1[N], s2[N];
int nxt[N];
void pre(char *s) {
nxt[0] = -1;
int j = 0, k = -1, l = strlen(s);
while (j < l - 1) {
if (k == -1 || s[j] == s[k]) {
nxt[++ j] = ++ k;
} else {
k = nxt[k];
}
}
}
int kmp(char *a, char *b) {
int l1 = strlen(a), l2 = strlen(b);
int i = 0, j = 0, ans = 0;
while (i < l1 && j < l2) {
if (j == -1 || a[i] == b[j]) {
++ i, ++ j;
} else {
j = nxt[j];
}
if (j == m2) {
++ ans;
j = 0;
}
}
return ans;
}
int main() {
while (scanf("%s", s1)) {
m1 = strlen(s1);
if (m1 == 1 && s1[0] == '#')
break;
scanf("%s", s2);
m2 = strlen(s2);
pre(s2);
printf("%d\n", kmp(s1, s2));
}
return 0;
}
「Power Strings」
题目传送门:Power Strings
思路:循环节(循环节的长度 \(= n - \text{nxt}_n\)),在特判一下循环节的长度是否能把字符串长度整除
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int nxt[10000010];
char S[10000010];
void pre(char *s) {
nxt[0] = -1;
int j = 0, k = -1, l = strlen(s);
while (j < l) {
if (k == -1 || s[j] == s[k]) {
nxt[++ j] = ++ k;
} else {
k = nxt[k];
}
}
}
int main() {
while (scanf("%s", S)) {
int n = strlen(S);
if (n == 1 && S[0] == '.')
break;
pre(S);
if (n % (n - nxt[n])) {
puts("1");
} else {
printf("%d\n", n / (n - nxt[n]));
}
}
return 0;
}
「Radio Transmission」
题目传送门:Radio Transmission
思路:循环节裸体,直接输出循环节长度就行了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int n;
int nxt[N];
char S[N];
void pre(char *s) {
nxt[0] = -1;
int j = 0, k = -1;
while (j < n) {
if (k == -1 || s[j] == s[k]) {
nxt[++ j] = ++ k;
} else {
k = nxt[k];
}
}
}
int main() {
scanf("%d", &n);
scanf("%s", S);
pre(S);
printf("%d\n", n - nxt[n]);
return 0;
}
「OKR-Periods of Words」
题目传送门:OKR-Periods of Words
思路:根据 nxt
数组的性质,一直找 nxt
,同时更新一下(路径压缩)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int n;
int nxt[N];
char S[N];
void pre(char *s) {
nxt[0] = -1;
int j = 0, k = -1;
while (j < n) {
if (k == -1 || s[j] == s[k]) {
nxt[++ j] = ++ k;
} else {
k = nxt[k];
}
}
}
int main() {
ll ans = 0;
scanf("%d", &n);
scanf("%s", S);
pre(S);
for (int i = 0, j; i <= n; ++ i) {
j = i;
while (nxt[j] > 0) {
j = nxt[j];
}
if (nxt[i] > 0)
nxt[i] = j;
ans += i - j;
}
printf("%lld\n", ans);
return 0;
}
「似乎在梦中见过的样子」
题目传送门:似乎在梦中见过的样子
思路:预处理出 nxt
数组,查看每个位置是否有 nxt
,且 nxt
要符合要求(距离要求),最后统计个数即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10000010;
int n, m, ans;
int nxt[N], ex[N], dp[N];
char S[N];
void pre_nxt(int l, char *s) {
nxt[1] = 0;
int k = 0, j = 1;
while (j + l <= m) {
if (k == 0 || s[l + j] == s[l + k]) {
nxt[++ j] = ++ k;
} else {
k = nxt[k];
}
if (dp[k])
dp[j] = dp[k];
else {
if (k >= n)
dp[j] = k;
else
dp[j] = 0;
}
}
for (int i = 1; i <= m - l; ++ i) {
if (dp[i] && (dp[i] << 1) + 1 <= i) {
++ ans;
}
}
}
int main() {
scanf("%s", S + 1);
scanf("%d", &n);
m = strlen(S + 1);
for (int i = 1; i <= m; ++ i) {
pre_nxt(i - 1, S);
}
printf("%d\n", ans);
return 0;
}
「Censoring」
题目传送门:Censoring
思路:把字符一个一个放入栈里,如果匹配了,就把这段字符从栈中取出,对每个位置的字符维护一个
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int l1, l2, cnt;
char s1[N], s2[N];
int nxt[N], dp[N], sta[N];
void pre(char *s) {
nxt[1] = 0;
int j = 0;
for (int i = 2; i <= l2; ++ i) {
while (j && s[i] != s[j + 1]) {
j = nxt[j];
}
if (s[i] == s[j + 1]) {
++ j;
}
nxt[i] = j;
}
}
void kmp(char *a, char *b) {
cnt = 0;
for (int i = 1, j = 0; i <= l1; ++ i) {
while (j && a[i] != b[j + 1])
j = nxt[j];
if (a[i] == b[j + 1])
j ++;
dp[i] = j;
sta[++ cnt] = i;//入栈
if (j == l2) //如果匹配成功,弹出,并更新j值
cnt -= l2, j = dp[sta[cnt]];
}
}
int main() {
scanf("%s\n%s", s1 + 1, s2 + 1);
l1 = strlen(s1 + 1), l2 = strlen(s2 + 1);
pre(s2);
kmp(s1, s2);
for (int i = 1; i <= cnt; ++ i)//大功率输出
printf("%c", s1[sta[i]]);
return 0;
}
朝气蓬勃 后生可畏