KMP算法概念
KMP算法是一种用于字符串匹配的经典算法,它的全名是Knuth-Morris-Pratt算法,是由Donald Knuth、Vaughan Pratt和James H. Morris分别独立发明的。KMP算法用于在一个主文本字符串中查找一个模式字符串的出现,它的主要优势在于在匹配失败时避免不必要的回溯。
KMP算法通过构建部分匹配表(Partial Match Table),也称为最长前缀后缀匹配表(Longest Prefix Suffix Table),来避免这种不必要的比较。
性能分析
KMP算法的时间复杂度为O(m + n),其中m是主文本字符串的长度,n是模式字符串的长度。
参考
算法学习笔记(13): KMP算法
模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static int [] getPmt(char [] pattern) { int pmt[] = new int [pattern.length]; for (int i = 1 , j = 0 ; i < pattern.length; i++) { while (j != 0 && pattern[i] != pattern[j]) j = pmt[j - 1 ]; if (pattern[i] == pattern[j]) j++; pmt[i] = j; } return pmt; } public static int count (char [] s, char [] p, int [] pmt) { int res = 0 ; for (int i = 0 , j = 0 ; i < s.length; i++) { while (j != 0 && s[i] != p[j]) j = pmt[j - 1 ]; if (s[i] == p[j]) j++; if (j == p.length) { res += 1 ; j = pmt[j - 1 ]; } } return res; }
题目描述
给出两个字符串 s 1 s_1 s 1 和 s 2 s_2 s 2 ,若 s 1 s_1 s 1 的区间 [ l , r ] [l, r] [ l , r ] 子串与 s 2 s_2 s 2 完全相同,则称 s 2 s_2 s 2 在 s 1 s_1 s 1 中出现了,其出现位置为 l l l 。
现在请你求出 s 2 s_2 s 2 在 s 1 s_1 s 1 中所有出现的位置。
定义一个字符串 s s s 的 border 为 s s s 的一个非 s s s 本身 的子串 t t t ,满足 t t t 既是 s s s 的前缀,又是 s s s 的后缀。
对于 s 2 s_2 s 2 ,你还需要求出对于其每个前缀 s ′ s' s ′ 的最长 border t ′ t' t ′ 的长度。
输入格式
第一行为一个字符串,即为 s 1 s_1 s 1 。
第二行为一个字符串,即为 s 2 s_2 s 2 。
输出格式
首先输出若干行,每行一个整数,按从小到大的顺序 输出 s 2 s_2 s 2 在 s 1 s_1 s 1 中出现的位置。
最后一行输出 ∣ s 2 ∣ |s_2| ∣ s 2 ∣ 个整数,第 i i i 个整数表示 s 2 s_2 s 2 的长度为 i i i 的前缀的最长 border 长度。
样例 #1
样例输入 #1
样例输出 #1
提示
样例 1 解释
。
对于 s 2 s_2 s 2 长度为 3 3 3 的前缀 ABA
,字符串 A
既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 1 1 1 。
数据规模与约定
本题采用多测试点捆绑测试,共有 3 个子任务 。
Subtask 1(30 points):∣ s 1 ∣ ≤ 15 |s_1| \leq 15 ∣ s 1 ∣ ≤ 15 ,∣ s 2 ∣ ≤ 5 |s_2| \leq 5 ∣ s 2 ∣ ≤ 5 。
Subtask 2(40 points):∣ s 1 ∣ ≤ 1 0 4 |s_1| \leq 10^4 ∣ s 1 ∣ ≤ 1 0 4 ,∣ s 2 ∣ ≤ 1 0 2 |s_2| \leq 10^2 ∣ s 2 ∣ ≤ 1 0 2 。
Subtask 3(30 points):无特殊约定。
对于全部的测试点,保证 1 ≤ ∣ s 1 ∣ , ∣ s 2 ∣ ≤ 1 0 6 1 \leq |s_1|,|s_2| \leq 10^6 1 ≤ ∣ s 1 ∣ , ∣ s 2 ∣ ≤ 1 0 6 ,s 1 , s 2 s_1, s_2 s 1 , s 2 中均只含大写英文字母。
KMP算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import java.util.Scanner;public class Main { public static int [] getPmt(char [] pattern) { int pmt[] = new int [pattern.length]; for (int i = 1 , j = 0 ; i < pattern.length; i++) { while (j != 0 && pattern[i] != pattern[j]) j = pmt[j - 1 ]; if (pattern[i] == pattern[j]) j++; pmt[i] = j; } return pmt; } public static int count (char [] s, char [] p, int [] pmt) { int res = 0 ; for (int i = 0 , j = 0 ; i < s.length; i++) { while (j != 0 && s[i] != p[j]) j = pmt[j - 1 ]; if (s[i] == p[j]) j++; if (j == p.length) { res += 1 ; System.out.println(i - j + 2 ); j = pmt[j - 1 ]; } } return res; } public static void main (String[] args) { Scanner in = new Scanner (System.in); while (in.hasNext()) { char s[] = in.next().toCharArray(), p[] = in.next().toCharArray(); int [] pmt = getPmt(p); count(s, p, pmt); for (int i = 0 ; i < p.length; i++) { System.out.print(pmt[i] + " " ); } } } }