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 }

 

posted @ 2022-02-12 16:28  没有你哪有我  阅读(81)  评论(0编辑  收藏  举报