1 模式匹配问题
题目概述
给定字符串text
和pattern
,在字符串text
中找出字符串pattern
出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
输入样例
h e l l o hello hello
l l ll ll
输出样例
2 2 2
解决方法
- KMP算法
建立数组next[]
,其中next[j]
代表当子串t[i],t[i+1],...,t[i+j-1]
与子串p[0],p[1],...,p[j-1]
匹配而t[i+j]
与p[j]
不匹配时,匹配位置回溯的位置。
next[j]
的值为字符串p[0],p[1],...p[j-1]
的最长的相同前缀后缀的长度。可以通过递推确定数组next[]
的值:
记k = next[j]
,①若p[k] == p[j]
,则next[j + 1] = k + 1
;②若p[k] != p[j]
,则令k = next[k]
重复上述步骤。
时间复杂度 O ( m + n ) O(m+n) O(m+n)
void Getnext(char* p,int next[]) {
int m = strlen(p);
next[0] = -1;
int k = -1, j = 0;
while (j < m - 1) {
if (k == -1 || p[k] == p[j]) {
j++; k++;
next[j] = k;
}
else {
k = next[k];
}
}
}
int KmpSearch(char* t, char* p) {
int n = strlen(t), m = strlen(p);
int i = 0, j = 0;
while (i < n && j < m) {
if (j == -1 || s[i] == p[j]) {
i++; j++;
}
else {
j = next[j];
}
}
if (j == m)
return i - j;
else
return -1;
}
- BM算法
坏字符规则:当发现不匹配字符p[i]
时,记该字符为c
,查找c
在字符串p[]
中出现的最靠近i
的左侧的位置,记为j
,若p[i]
左侧不存在c
,则记j = -1
。则模式串移动的长度为i - j
。
好后缀规则:当发现相匹配的后缀p[i],p[i + 1],...,p[m - 1]
时,依次查找以p[i],p[i + 1],...p[m - 1]
开头的子后缀在字符串p[]
中出现的最靠近其左侧的位置,记为j
,若上述子后缀均不存在,则记j = -1
。若查找到的子后缀以p[k]
开头,则模式串移动的长度为k - j
。
在这里插入代码片
2 最长回文子串问题
题目概述
给定一个字符串,输出其最长回文子串
输入样例
a b a c d abacd abacd
输出样例
a b a aba aba
解决方法
- Manacher算法
为了避免讨论回文串长度的奇偶性,对字符串s[]
进行预处理,在每个字符两边加上#号。对于处理后的字符串,建立数组p[]
,其中p[i]
代表以s[i]
为对称中心的回文串的半径。可以通过递推关系确定数组p[]
的值:
记以s[0],s[1],...,s[i-1]
为对称中心的回文子串的右边界的最大值为r
,对应的回文子串的对称中心为c
。若r <= i
,则通过循环确定p[i]
。若r > i
,记i
关于c
的对称点为j
,r
关于c
的对称点为l
。①若p[j] =< j - l + 1
,则p[i] = p[j]
;②若p[j] > j - l + 1
,则令p[i] = r - i
,再通过循环确定p[i]
。
void Manacher(char* s, int p[]) {
int n = strlen(s);
p[0] = 1;
int c = 0, r = 0;
int max_p = p[0], max_i = 0;
for (int i = 1; i < n; i++) {
if (r > i) {
int j = 2 * c - i;
p[i] = min(p[j], r - i);
}
else {
p[i] = 1;
}
while (s[i + p[i]] == s[i - p[i]]) {
p[i]++;
}
if (r < i + p[i]) {
c = i;
r = i + p[i];
}
if (p[i] > max_p) {
max_p = p[i];
max_i = i;
}
}
for (int i = max_i - max_p + 2; i <= max_i + max_p - 2; i += 2) {
cout << s[i];
}
cout << endl;
}