| 831. KMP字符串 | 字符串 | KMP |
|---|---|---|
给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串 P 在模式串 S 中多次作为子串出现。
求出模板串 P 在模式串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。
数据范围
1≤N≤105
1≤M≤106
输入样例:
3aba5ababa
输出样例:
0 2
算法思想:
相较于暴力算法,利用已经匹配好的部分子串的信息,不回退模板串,而只回退模式串。
当然回退多少需要next数组来帮助,含义是非平凡(不是原串自身)最长公共前后缀的长度
时间复杂度 O(m + n)
空间复杂度 O(n)
import java.util.*;import java.io.*;public class Main {public static void main(String[] sss) throws IOException{// Scanner sc = new Scanner(new InputStreamReader(System.in));BufferedReader br = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));int n = Integer.parseInt(br.readLine());String p = br.readLine();int m = Integer.parseInt(br.readLine());String s = br.readLine();int[] ne = new int[n];//模式串与模式串匹配求ne。 本质是dp//定义k-max(str)为字符串str中前缀与后缀相同的最大长度。// 例如 str = "abcab", k-max(str) = 2//定义sub(str, n) 为字符串str从0开始, 到n结束的子串,左闭右开[0, n),即子串长度为n//所以有 k-max(sub(str, n))表示str的从0开始的长度为n的子串的最长相同前后缀。//ne[i] = k-max(sub(str, i+1))//也就是说ne是这样一个数组,数组中的每个元素存储了模式串对应位置的子串的最长前后缀。//ne[0] = k-max(sub(str, 1))//ne[1] = k-max(sub(str, 2))//i 指向模板串, j 指向模式串 (相对来说!!)//而且i和j都指向待比较字符//模式串在运行过程中就相当于是前缀,而模板串就是后缀、//注意初始化i=1,不能写成i=0,否则会导致ne[0] = 1,从而导致一连串错误for (int i = 1, j = 0; i < n; i++) {//如果当前位置不匹配,j只能回退到能匹配到的最近位置//while的两个结束条件://1. 最坏情况,j == 0, 退到开头(第一个字符), 退无可退。//2. 当前待比较字符不同while (j > 0 && p.charAt(i) != p.charAt(j)) j = ne[j - 1];//这个if语句的含义是://判断上面的while因何种原因退出//1. 因j > 0 不满足而退出说明此时j == 0,用if判断最长前后缀是1还是0//2. 因去p[i] != q[j]退出说明此时p[i]==p[j],if结果为真if (p.charAt(i) == p.charAt(j)) j++;//更新当前子串最长前后缀ne[i] = j;// 小小的优化// 在匹配第i+1个字符时如果不匹配,需要找ne[i]继续匹配// 如果p[i+1]==p[ne[i]],下一次仍不匹配,需要找ne[ne[i]]继续匹配if (i + 1 < m && p.charAt(i + 1) == p.charAt(j))ne[i] = ne[j];}//正式进行字符串匹配for (int i = 0, j = 0; i < m; i++) {while (j > 0 && s.charAt(i) != p.charAt(j)) j = ne[j - 1];if (s.charAt(i) == p.charAt(j)) j++;if (j == n) {j = ne[j - 1];bw.write(i - n + 1 + " ");}}br.close();bw.close();}}
更多例题:
| 459. 重复的子字符串 | KMP的应用,找循环节 可以用KMP或者Z函数 |
|
|---|---|---|
| 结论:若S是周期串,则 最小循环节长度为 len <==> s[0, n - 1 - len] = s[len, n - 1] |
||
| AcWing. 141. 周期 | KMP的应用,459的加强版 只会用KMP,Z函数的解法感觉很麻烦 |
|
| 214. 最短回文串 | KMP的应用,找最长回文前缀 | |
