Loading

「刷题记录」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;
}
posted @ 2023-01-25 16:45  yi_fan0305  阅读(36)  评论(0编辑  收藏  举报