KMP算法思路及其实现(Java)

题目描述
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P在字符串 S中多次作为子串出现。

求出模式串 P在字符串 S中所有出现的位置的起始下标。

输入格式
第一行输入整数 N,表示字符串 P的长度。

第二行输入字符串 P。

第三行输入整数 M,表示字符串 S的长度。

第四行输入字符串 S。

输出格式
共一行,输出所有出现位置的起始下标(下标从 0开始计数),整数之间用空格隔开。

数据范围
1≤N≤105

1≤M≤106

样例
输入样例:
3
aba
5
ababa
输出样例:
0 2

时间复杂度
构建ne数组的时间复杂度:
在构建ne数组的循环中,i从2到n,其中n是模式串的长度,因此这部分的时间复杂度为O(n)。

KMP匹配的时间复杂度:
在KMP匹配的循环中,i从1到m,其中m是文本串的长度。
内部while循环执行次数取决于j的值,j最大不会超过n(模式串的长度),因此内部while循环的总体执行次数是O(m)。
因此,KMP匹配的时间复杂度也是O(m)。
综合来看,整个程序的时间复杂度是O(n+m),其中n是模式串的长度,m是文本串的长度。这是KMP算法的优点之一,它具有线性的时间复杂度,相对于朴素的字符串匹配算法具有更高的效率,特别是在大文本串中查找多个匹配的情况下。

import java.io.*;

public class Main {
    static int n, m;
    static int N = 1000010;
    static int[] ne = new int[N];
    static char[] p;
    static char[] s;
    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        n = Integer.parseInt(br.readLine());
        String pT = br.readLine();
        char[] p = new char[N];
        for (int i = 1; i <= n; i++) {
            p[i] = pT.charAt(i - 1);
        }
        m = Integer.parseInt(br.readLine());
        String sT = br.readLine();
        char[] s = new char[N];
        for (int i = 1; i <= m; i++) {
            s[i] = sT.charAt(i - 1);
        }

        //计算ne数组
        //ne数组是计算前面的字符和后面的字符相等的个数
        //例如:abababab
        //在ne[3]的时候,第一个字母a和第三个也就是ne[3]的最后一个a相等,但ab和ba不相等,所以相ne[3]=1
        //在ne[4]的时候,第一个字母a和ne[4]的最后一个字母b不相等,但1~2的ab和3~4的ab相等,所以ne[4]=2
        //同理ne[5]=3因为1~3的aba和3~5的aba相等
        //i从2开始因为第一个没法比较默认就是0
        for (int i = 2, j = 0; i <= n; i++) {
            //如果出现一个数与j + 1这个数不相等,就让j等于上一个数的ne
            while(j > 0 && p[i] != p[j + 1]) j = ne[j];
            //如果相等就让j++
            if(p[i] == p[j + 1]) j ++;
            ne[i] = j;
        }

        //KMP实现
        //例如:abababab
        for (int i = 1, j = 0; i <= m; i++) {
            //j还在0的时候如果赋值就可能造成跳过一些数
            //如果两个数组同位置比较不相同的话就让j移到j的ne的位置因为ne前部分和后部分相等,且ne后半部分与与大数组上面两相等
            //例如abababc
            //   ababc
            while(j > 0 && s[i] != p[j + 1]) j = ne[j];
            if(s[i] == p[j + 1]) j ++;
            //如果相等的个数等于需要比较的模板数组的长度,说明该输出了
            if(j == n) {
                bw.write((i - n) + " ");
                //输出完后继续向下比较,所以要让j = 上一个数的ne
                j = ne[j];
            }
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值