AcWing刷题——KMP字符串(经典)
题目描述
给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串 P 在模式串 S 中多次作为子串出现。
求出模板串 P 在模式串 S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P 。
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S 。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。
数据范围
1 <= N <= 10^5
1 <= M <= 10^6
输入样例:
3 aba 5 ababa
输出样例:
0 2
Java代码
1 import java.util.*; 2 import java.io.*; 3 4 public class Main { 5 6 static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 7 8 static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)); 9 10 static int N = 100000 + 10; 11 12 static int M = 1000000 + 10; 13 14 static int[] p = new int[N]; 15 16 static int[] s = new int[M]; 17 18 static int[] next = new int[N]; 19 20 static int n; 21 22 static int m; 23 24 /** 25 * 求next数组 26 */ 27 public static void next() { 28 // next[1] = 0; 29 for (int i = 2, j = 0; i <= n; i ++ ) { 30 // 跟下一个去匹配,所以这里要j + 1, 31 // j = next[j] 可以认为是退而求其次,更长的我们匹配不了,就匹配更短一些的字符 32 // 就像y总说的一样,硬刚不行,则退一步,以退为进(做人做事也是这个道理),俗话说的好:退一步海阔天空 33 // 如果退到退无可退了,即 j == 0时,则让模式串先行一步(i++),然后再继续从头开始匹配 34 while (j > 0 && p[i] != p[j + 1]) j = next[j]; 35 if (p[i] == p[j + 1]) { 36 // 匹配成功之后,两人共同前进一步 37 j++; 38 } 39 // j表示当前匹配成功的长度(即最长公共前后缀) 40 next[i] = j; 41 } 42 } 43 44 public static void main(String[] args) throws Exception{ 45 n = Integer.parseInt(reader.readLine()); 46 char[] c1 = reader.readLine().toCharArray(); 47 m = Integer.parseInt(reader.readLine()); 48 char[] c2 = reader.readLine().toCharArray(); 49 50 for (int i = 1; i <= n; i++ ) { 51 p[i] = c1[i - 1]; 52 } 53 for (int i = 1; i <= m; i ++ ) { 54 s[i] = c2[i - 1]; 55 } 56 57 next(); 58 59 // for (int i = 1; i <= n; i++ ) { 60 // writer.write(next[i] + " "); 61 // } 62 63 // writer.write("拉拉\n"); 64 65 // j表示当前的匹配位置(不是下标,因为是按1开始遍历的) 66 for (int i = 1, j = 0; i <= m; i++ ) { 67 while (j > 0 && s[i] != p[j + 1]) j = next[j]; 68 if (s[i] == p[j + 1]) { 69 // 跟模式串共同往后移动一位 70 j++; 71 } 72 // 如果当前位置,模板串p跟模式串s相同,且j == 0,则仅仅让模式串s向后移动一位,因为此时第一个就匹配失败了 73 /* 74 j > 0,代表此时模板串中已经跟模式串s部分匹配(且一定是前半部分) 75 如果是这种情况的话,按照我们人类的想法,如果有匹配的我们就让模 76 板串和模式串都往后移动一位,如果后一位匹配匹配不上,则让模板串p 77 通过next数组跳到当前最大公共前后缀的后一位,继续跟模式串的当前位 78 置的字符进行比较,重复以上操作。 79 */ 80 // 判断此时是否已经匹配成功,即j == n(表示匹配成功的长度已经等于模板串的长度) 81 if (j == n) { 82 writer.write(i - n + " "); 83 j = next[j]; 84 } 85 } 86 writer.flush(); 87 } 88 89 }