KMP算法基础
AcWing 831. KMP字符串
给定一个模式串 S S S,以及一个模板串 P P P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模板串 P P P 在模式串 S S S 中多次作为子串出现。
求出模板串 P P P 在模式串 S S S 中所有出现的位置的起始下标。
输入格式
第一行输入整数 N N N,表示字符串 P P P 的长度。
第二行输入字符串 P P P。
第三行输入整数 M M M,表示字符串 S S S 的长度。
第四行输入字符串 S S S。
输出格式
共一行,输出所有出现位置的起始下标(下标从 0 0 0 开始计数),整数之间用空格隔开。
数据范围
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1≤N≤105
1 ≤ M ≤ 1 0 6 1≤M≤10^6 1≤M≤106
输入样例:
3
aba
5
ababa
输出样例:
0 2
时/空限制: 1s / 256MB
来源: 模板题
算法标签:KMP
yxc’s Solution
KMP的思考方式和一般的单调队列与双指针算法是类似的,或者说和一般的做题思路是类似的.
- 暴力算法怎么做;
- 如何去优化.
- 暴力枚举:
s[N] // 要进行比较的字符串 p[M] // 模板串 for (int i = 1; i <= n; i ++ ) { bool flag = true; // 表示匹配是否成功 for (int j = 1; j <= m; j ++ ) if (s[i + j - 1] != p[j]) { flag = false; break; } }
- KMP习惯字符串下标从 1 1 1 开始(当然,从 0 0 0 开始也有对应的写法).
-
考虑优化:
- 假设字符串 s s s 从 i i i 这个位置开始匹配,匹配到 i + k i+k i+k 这个位置之前都有 s i ’ = p j ’ , i ′ ∈ [ i , i + k ) , j ′ ∈ [ 1 , 1 + k ) s_{i’}=p_{j’},\;i'\in[i,i+k),\;j'\in[1,1+k) si’=pj’,i′∈[i,i+k),j′∈[1,1+k);
- 当 s i + k ≠ p 1 + k s_{i+k} \not= p_{1+k} si+k=p1+k 时,如果是暴力做法,则会让 i i i 往后移动一次并重新匹配;
- 由于已经匹配了这么多了,所以是有一些额外信息在里面,如果可以利用这些额外信息,就可以帮助我们少枚举一些东西;
-
考虑 模式串 P P P 最少往后移动多少,可以继续从 i + k i+k i+k 这个位置匹配:
图 1- 如果模式串 P P P 能够往后移动 x x x 次,说明 s i ’ = p j ’ , i ′ ∈ [ i + x , i + k ) , j ′ ∈ [ 1 , 1 + k − x ) s_{i’}=p_{j’},\;i'\in[i+x,i+k),\;j'\in[1,1+k-x) si’=