28.Acwing基础课第831题-简单-KMP
28.Acwing基础课第831题-简单-KMP.md
题目描述
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P 在字符串 S 中多次作为子串出现。
求出模式串 P 在字符串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。
数据范围
1≤N≤105
1≤M≤106
输入样例:
3
aba
5
ababa
输出样例:
0 2
代码:
#include <stdio.h>
const int N = 100010; // 模式串 p 的最大长度 + 10
const int M = 1000010; // 主串 s 的最大长度 + 10
char p[N]; // 模式串(要找的字符串)
char s[M]; // 主串(被搜索的字符串)
int n, m; // n:模式串长度 m:主串长度
int ne[N]; // next数组,存储模式串每个位置的最长相等前后缀长度
// 计算next数组(包括next[n+1]用于匹配成功后的滑动)
void get_next()
{
// i:从前往后遍历模式串的指针(后缀指针)
// j:表示当前已经匹配成功的【最长相等前后缀】长度(前缀指针)
int i = 1, j = 0;
ne[1] = 0; // 第一个字符没有真前后缀,最长相等长度固定为0
// 循环条件:i 遍历到模式串最后一个字符
// 循环结束后会算出 ne[n+1],用于匹配成功后快速滑动
while (i <= n)
{
// 情况1:j == 0 表示没有任何匹配的前后缀,必须从头开始
// 情况2:p[i] == p[j] 表示当前字符能继续匹配,前后缀可以延长
if (j == 0 || p[i] == p[j])
{
++i; // 后缀指针后移
++j; // 匹配长度 +1
ne[i] = j; // 将当前最长匹配长度存入 next 数组
}
// 不匹配:回退 j,利用已有的 next 数组找更短的前后缀
else j = ne[j];
}
}
// KMP 匹配函数:在主串 s 中寻找所有模式串 p 出现的位置
void kmp_match()
{
int i = 1, j = 1; // i:主串指针 j:模式串指针
int first = 1; // 控制输出格式:第一次输出不加空格,后面输出加空格
// 遍历主串,i 只前进不回退,这是 KMP 高效的原因
while (i <= m)
{
// 情况1:j == 0 表示模式串已退到开头,重新开始匹配
// 情况2:s[i] == p[j] 表示当前字符匹配成功,两个指针都后移
if (j == 0 || s[i] == p[j])
{
++i; // 主串指针后移
++j; // 模式串指针后移
}
// 当前字符不匹配:模式串指针按 next 数组回退,主串指针不动
else j = ne[j];
// ====================== 匹配成功 ======================
// j 走到 n+1:说明模式串 p[1]~p[n] 全部匹配完成
if (j == n + 1)
{
// 计算匹配位置:
// i 是主串当前指针,已经后移过
// i - n - 1 是模式串在主串中【0开始】的起始下标
int pos = i - n - 1;
// 格式化输出:第一个位置前面不加空格,其他位置前面加空格
if (first)
{
printf("%d", pos);
first = 0;
}
else
{
printf(" %d", pos);
}
// 匹配成功后,继续寻找下一个可能的匹配
// 利用 ne[n+1] 让模式串快速滑动,不用从头开始
j = ne[j];
}
}
}
int main()
{
// 输入模式串长度
scanf("%d", &n);
// 输入模式串,从下标 1 开始存储(方便 KMP 逻辑)
scanf("%s", p + 1);
// 输入主串长度
scanf("%d", &m);
// 输入主串,从下标 1 开始存储
scanf("%s", s + 1);
// 第一步:预处理模式串,求 next 数组
get_next();
// 第二步:KMP 匹配,输出所有出现位置
kmp_match();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
const int N = 1e5+10;//模式串
const int M = 1e6 + 10;//主串
// 定义结构体:ch[1]开始存储字符,length存储实际长度
typedef struct
{
char ch[1000010 + 1]; // 足够容纳主串(模式串也用此大小)
int length;
} SString;
const int MAXN = 100010; // nextval数组大小(模式串最大长度+10)
// 计算模式串T的nextval数组(包括T.length+1)
void get_nextval(SString T, int nextval[])
{
int i = 1, j = 0;
nextval[1] = 0;
while (i <= T.length) // 循环到T.length,确保求出nextval[T.length+1]
{
if (j == 0 || T.ch[i] == T.ch[j])
{
++i; ++j;
if (T.ch[i] != T.ch[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else
j = nextval[j];
}
}
// KMP匹配,从pos开始(pos=1),输出所有匹配位置(下标从0)
int Index_KMP(SString S, SString T, int pos)
{
int nextval[MAXN];
get_nextval(T, nextval);
int i = pos, j = 1;
int first = 1; // 控制输出格式
while (i <= S.length)
{
if (j == 0 || S.ch[i] == T.ch[j])
{
++i; ++j;
}
else j = nextval[j];
if (j == T.length + 1) // 匹配成功
{
int start = i - T.length - 1; // 0-based下标
if (first)
{
printf("%d", start);
first = 0;
}
else printf(" %d", start);
j = nextval[j]; // 利用nextval[T.length+1]滑动,继续匹配
}
}
return 0; // 返回值无实际意义
}
// 全局变量,避免栈溢出
SString P, S;
int main()
{
int n, m;
// 输入模式串
scanf("%d", &n);
getchar();
for (int k = 1; k <= n; k++)
scanf("%c", &P.ch[k]);
P.length = n;
// 输入主串
scanf("%d", &m);
getchar();
for (int k = 1; k <= m; k++)
scanf("%c", &S.ch[k]);
S.length = m;
// 执行匹配
Index_KMP(S, P, 1);
return 0;
}

浙公网安备 33010602011771号