KMP

实现方法有很多,简单介绍一种

ababababab
0012345678

abababa
abababa
ababa
ababa
aba
aba
a
a

ababababcdddddd
ababababab
0012345678
ababababab

abacabadabacaba
001012301234567

abacabadabacabad
0010123012345678

abacabadabacabac
0010123012345674

abacabadabacabab
0010123012345672

abacabadabacabaa
0010123012345671

abacabadabacabae
0010123012345670

对于每个前缀 s[1..i]
找到最长的前缀等于后缀的长度p[i](不能是整个前缀
p[i] < i
s[1..p[i]] == s[i-p[i]+1..i]
长度为p[i]的前缀 以i结尾长度为p[i]的后缀
最长的是p[i]
如果希望找到所有合法的解
p[i]
p[p[i]]
p[p[p[i]]]
....
直到0

一些人的定义/实现,在求p[i]时,不仅要求
s[1..p[i]] == s[i-p[i]+1..i]
还要求s[p[i]+1] != s[i]
这是没有必要的,我们不采用这种写法

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaab
aaaaaaaaab
aaaaaaaaab

abcabcabcabcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc

参考代码

#include <bits/stdc++.h>
using namespace std;
char s[1000020];
char t[1000020];
int p[1000020];
int main() {
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    int n = strlen(s + 1);
    int m = strlen(t + 1);
    int u = 0;
    for (int i = 2; i <= m; i++) {
        while (u > 0 && t[u + 1] != t[i]) {
            u = p[u];
        }
        if (t[u + 1] == t[i]) {
            u++;
        }
        p[i] = u;
    }
    u = 0;
    for (int i = 1; i <= n; i++) {
        while (u > 0 && t[u + 1] != s[i]) {
            u = p[u];
        }
        if (t[u + 1] == s[i]) {
            u++;
        }
        if (u == m) {
            printf("%d\n", i - m + 1);
            u = p[u]; // 找到的匹配允许有重叠
            // u = 0; // 找到的匹配不允许有重叠
        }
    }
    for (int i = 1; i <= m; i++) {
        printf("%d%c", p[i], i == m ? '\n' : ' ');
    }
}

P4391

cabcabca
00012345
bca
bca
ca

P3435

寻找最长的周期
不能是整个串本身

babababa 6
bababa

bababab 6
bababa

bababa 4
baba

babab 4
baba

baba 2
ba

bab 2
ba

ba 0
ba

b 0

#include <bits/stdc++.h>
using namespace std;
char s[1000020];
int p[1000020];
int f[1000020];
int n;
long long z;
int main() {
	scanf("%d", &n);
	scanf("%s", s + 1);
	int u = 0;
	f[1] = 1;
	for (int i = 2; i <= n; i++) {
		while (u > 0 && s[u + 1] != s[i]) {
			u = p[u];
		}
		if (s[u + 1] == s[i]) {
			u++;
		}
		p[i] = u;
		f[i] = i;
		if (p[i] > 0)
		{
			f[i] = f[p[i]];
		}
		z += i - f[i];
	}
	printf("%lld\n", z);
	return 0;
}

对于每个位置,按照p[i]往0跳,f[i]是最后一步是从多少跳到0的

hdu 1711 Number Sequence

http://acm.hdu.edu.cn/showproblem.php?pid=1711
输入两个数字串匹配,找到第一次匹配的位置。
将KMP算法由字符串,推广到一般的数字序列。

hdu 2087 剪花布条

http://acm.hdu.edu.cn/showproblem.php?pid=2087

输入两个字母串匹配,问第二个在第一个中出现多少次。
字符串匹配,求匹配到的子串个数,不能重叠,问最多匹配多少个。
比如aaaaaaaaa中包含3个aaa,而不是7个。

hdu 2594 Simpsons’ Hidden Talents

http://acm.hdu.edu.cn/showproblem.php?pid=2594

找到最长的一个字符串,既是第一个串的前缀,又是第二个串的后缀。
连接起来,求next数组。

poj 3461 Oulipo

http://poj.org/problem?id=3461

字符串匹配,求匹配到的子串个数,可以重叠

poj 2406 Power Strings

http://poj.org/problem?id=2406

输入一个字符串求最小循环节,实际输出最小循环节的次数。
对于字符串计算next数组,考虑n-next[n]是否是n的约数。

arc060_d

https://beta.atcoder.jp/contests/arc060/tasks/arc060_d

输入一个字符串,分成尽量少的段,使得每段没有循环节。
输出最小段数,和方案数。

只有三种情况

  1. KMP
    1. 参考代码
    1. P4391
    2. P3435
    3. hdu 1711 Number Sequence
    4. hdu 2087 剪花布条
    5. hdu 2594 Simpsons’ Hidden Talents
    6. poj 3461 Oulipo
    7. poj 2406 Power Strings
    8. arc060_d