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;
}
posted @ 2026-04-05 11:19  CodeMagicianT  阅读(2)  评论(0)    收藏  举报