KMP算法入门【详解+例题模板】

kmp算法的主要作用在于对next数组的运用

• 性质1：对于每一个长度len的子串，该子串的最小循环节为len-next[len]
• 性质2：kmp的next不断向前递归的过程可以保证对于每一个当前前缀，都有一段后缀与之对应

next[]数组的运用。

``````void getnext(const char *s){
int i = 0, j = -1;
nextval[0] = -1;
while(i != len)
{
if(j == -1 || s[i] == s[j])
nextval[++i] = ++j;
else
j = nextval[j];
}
}``````

next[i]代表了前缀和后缀的最大匹配的值（需要彻底明白这点，相当重要）

``````void getnext(const char *p) {//前缀函数(滑步函数)
int i = 0, j = -1;
nextval[0] = -1;
while(i != len)
{
if(j == -1 || p[i] == p[j]) //(全部不相等从新匹配 || 相等继续下次匹配)
{
++i, ++j;
if(p[i] != p[j]) //abcdabce
nextval[i] = j;
else //abcabca
nextval[i] = nextval[j];
}
else
j = nextval[j]; //子串移动到第nextval[j]个字符和主串相应字符比较
}
}``````

YY：

---------------------

求next数组的模板

``````static void getnext() {
int j=0,k=-1;
next[0] = -1;
while(j<m-1){
if(k==-1 || p[j]==p[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}``````

kmp的模板

``````static int kmp() {
int i=0,j=0;
while(i<n) {
if(j==-1 || s[i]==p[j]) {
i++;
j++;
} else
j = next[j];
if(j==m)
return i-m;//返回下标
}
return -1;
}``````

hdu1711

2

13 5

1 2 1 2 3 1 2 3 1 3 2 1 2

1 2 3 1 3

13 5

1 2 1 2 3 1 2 3 1 3 2 1 2

1 2 3 2 1

6

-1

``````import java.util.Scanner;

public class hdu1711 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
while(t-->0) {
n = in.nextInt();
m = in.nextInt();
s = new int[n];
p = new int[m];
next = new int[m];
for(int i=0;i<n;i++)
s[i] = in.nextInt();
for(int i=0;i<m;i++)
p[i] = in.nextInt();
getnext();
System.out.println(kmp());
}
}

static int[] next;
static int[] s;
static int[] p;
static int n,m;

static void getnext() {
int j=0,k=-1;
next[0] = -1;
while(j<m-1){
if(k==-1 || p[j]==p[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

static int kmp() {
int i=0,j=0;
while(i<n) {
if(j==-1 || s[i]==p[j]) {
i++;
j++;
} else
j = next[j];
if(j==m)
return i-m+1;//注意+1是因为下标是1开始的
}
return -1;
}

}``````

leetcode 28. 实现strStr()(Implement strStr())

``````class Solution {
public int strStr(String haystack, String needle) {
if(haystack==null || haystack.equals("") || haystack.length()<1)
return needle.equals("")?0:-1;
else if(needle==null || needle.equals("") || needle.length()<1)
return 0;
return match(needle.toCharArray(),haystack.toCharArray());
}

static int match(char[] p,char[] T) {
int[] next = buildNext(p);
int n = T.length,m = p.length,i = 0,j = 0;
while(i<n) {
if(j<0 || T[i]==p[j]) {
i++;
j++;
} else
j = next[j];
if(j==m)
return i-j;
}
return -1;
}
static int[] buildNext(char[] p) {
int m = p.length,j=0;
int[] N = new int[m];
int t=N[0]=-1;
while(j<m-1) {
if(0>t ||  p[j]==p[t]) {
j++;
t++;
N[j]=(p[j]!=p[t])?t:N[t];
}
else
t = N[t];
}
return N;
}
}``````

hdu1358

3

AAA

12

aabaabaabaab

0

2 2

3 3

2 2

6 2

9 3

12 4

推荐

JGShining | 我们已经为您精心挑选了几个类似的问题：   1686  3336  3746  3068  2203

``````  i      0  1  2  3  4  5  6  7  8  9  10 11
a[i]    a  a  b  a  a  b  a  a  b  a  a  b
next[i] -1  0  1  0  1  2  3  4  5  6  7  8   ``````

next[i]值是0或-1的忽略。

注意：由于输出次数太多 (2 <= N <= 1 000 000)，建议用printf输出，否则会超时。原文链接

``````import java.util.Scanner;

public class hdu1358 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = 0;
while(true) {
++t;
n = in.nextInt();
if(n==0)
break;
ch = in.next().toCharArray();
next = new int[n+5];//next最好多开点
next[0] = -1;
getnext();
System.out.println("Test case #"+t);
int j;
for(int i=0;i<=n;i++) {
if(next[i]==-1 || next[i]==0)
continue;
j = i - next[i];
if(i%j==0)
System.out.println(i+" "+i/j);
}
System.out.println();
}
}

static int[] next;
static char[] ch;
static int n;

static void getnext() {
int j=0,k=-1;
while(j<n) {
if(k==-1 || ch[j]==ch[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

}
``````

hdu2087

abcde a3

aaaaaa aa

0

3

``````import java.util.Scanner;

public class hdu2087 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str="";
while(true) {
str = in.next();
if(str.equals("#"))
break;
s = str.toCharArray();
p = in.next().toCharArray();
n = s.length;
m = p.length;
next = new int[m+1];
next[0] = -1;
getnext();
System.out.println(kmp());
}
}

static char[] s;
static char[] p;
static int[] next;
static int n,m;

static void getnext() {
int j=0,k=-1;
while(j<m) {
if(k==-1 || p[j]==p[k]) {
j++;
k++;
next[j] =k;
} else
k = next[k];
}
}

static int kmp() {
int i=0,j=0,ans=0;
while(i<n) {
if(j==-1 || s[i]==p[j]) {
i++;
j++;
} else
j = next[j];
if(j==m) {
j=0;
ans++;
}
}
return ans;
}

}
``````

hdu1686

Tout avait Pair normal，mais tout s'affirmait faux。Tout avait Fair normal，d'abord，puis surgissait l'inhumain，l'affolant。Il aurait voulusavoiroùs'articulaitl'association qui l'unissait au roman：stir son tapis，assaillantàtoutinstant son imagination，l'intuition d'un tabou，la vision d'un mal obscur，d'un quoi vacant ，d'un dit：la vision，l'avision d'unoubi commandant tout，oùs'abolissaitla raison：tout avait l'air normal mais ...

3

BAPC

BAPC

AZA

AZAZAZA

VERDI

AVERDXIVYERDIAN

1

3

0

lcy | 我们已经为您精心挑选了几个类似的问题：   3336  3746  2203  3068  2087

``````import java.util.Scanner;

public class hdu1686 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
while(t-->0) {
p = in.next().toCharArray();
s = in.next().toCharArray();
n = s.length;
m = p.length;
next = new int[m+1];
next[0] = -1;
getnext();
System.out.println(kmp());
}
}

static char[] s;
static char[] p;
static int[] next;
static int n,m;

static void getnext() {
int j=0,k=-1;
while(j<m) {
if(k==-1 || p[j]==p[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

static int kmp() {
int i=0,j=0,ans = 0;
while(i<n) {
if(j==-1 || s[i]==p[j]) {
i++;
j++;
} else
j = next[j];
if(j==m) {
j = next[j];
ans++;
}

}
return ans;
}

}``````

kmp+线性dp

问题描述

s：“abab”

1

4

ABAB

6

i~j之间已经不可能有以 j 结尾的子串是前缀了，不然next[j]就不是 i 了

``````import java.util.Scanner;

public class hdu3336 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
long ans;
while(t-->0) {
n = in.nextInt();
next = new int[n+1];
dp = new int[n+1];
next[0] = -1;
ans = 0;
s = in.next().toCharArray();
getnext();
for(int i=1;i<=n;i++)
dp[i] = 1;
for(int i=1;i<=n;i++) {
dp[i] = dp[next[i]]+1;
ans += dp[i];
ans %= 10007;
}
//			for(int i=0;i<=n;i++)
//				System.out.print(dp[i]+" ");
//			System.out.println();
//			for(int i=0;i<=n;i++)
//				System.out.print(next[i]+" ");
//			System.out.println();
System.out.println(ans);
}
}

static char[] s;
static int n;
static int[] next;
static int[] dp;
static void getnext(){
int j=0,k=-1;
while(j<n) {
if(k==-1 || s[j]==s[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

}
``````

hdu3746

KMP:补齐循环节

CC总是在本月底变得非常沮丧，昨天他已经检查了他的信用卡，没有任何意外，只剩下99.9元。他太苦恼了，想着如何度过最后的日子。受到“HDU CakeMan”的企业家精神的启发，他想出售一些小东西来赚钱。当然，这不是一件容易的事。

CC对他的想法感到满意并请求您的帮助。

3

aaa

abca

abcde

0

2

5

给你一个串,要你在串头或尾添加最少的字符,使得该串至少有2个循环节,问你最少需要加几个字符.

首先要明白:如果一个串需要至少添加x(x>=0)个字符才能是有>=2个循环节的串,那么我可以只在串末尾添加,不需要去串头添加.(比如串cabc,循环节是abc,我可以在尾部添加ab即可.)

首先如果原始串已经有至少两个循环节就不必添加.当f[m]>0&&m%(m-f[m])==0时,不必添加.(结合之前的KMP循环节题目看看是不是这样.)

现在假设条件 f[m]>0&&m%(m-f[m])==0 不成立的时候呢?

``````#include<cstdio>
#include<cstring>

using namespace std;

char s[100005];
int next[100005];
int n,t;

void getnext(){
int j=0,k=-1;
while(j<n){
if(k==-1 || s[j]==s[k]){
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

int main(){
scanf("%d",&t);
while(t--){
scanf("%s",&s);
n = strlen(s);
next[0]=-1;
getnext();
int j = n - next[n];
if(n%j==0 && n!=j)
printf("0\n");
else
printf("%d\n",j-n%j);
}

return 0;
}``````

leetcode 459. 重复的子字符串

kmp求循环节

``````class Solution {
static char[] p;
static int[] next;
static int n;

public static boolean repeatedSubstringPattern(String s) {
if(s=="" || s.length()<=1)
return false;
n = s.length();
p = s.toCharArray();
next = new int[n+1];
next[0]=-1;
int j=0,k=-1;
while(j<n) {
if(k==-1 || p[j]==p[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
//		for(int i=0;i<=n;i++)
//    		System.out.print(next[i]+" ");
//    	System.out.println();
int m = n - next[n];//长度为len的字符串的最小循环节为len-next[len]
return n!=m && n%m==0;
}
}``````

poj2406

``````abcd
aaaa
ababab
.
``````

``````1
4
3``````

n%j==0这个判断很重要哦

``````import java.util.Scanner;

public class poj2406 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(true) {
String str = in.next();
if(str.equals("."))
break;
s = str.toCharArray();
n = s.length;
next = new int[n+1];
next[0]=-1;
getnext();
int j = n - next[n];
System.out.println(n%j==0?n/j:1);
}
}

static char[] s;
static int[] next;
static int n;

static void getnext() {
int j=0,k=-1;
while(j<n) {
if(k==-1 || s[j]==s[k]) {
j++;
k++;
next[j] = k;
} else
k = next[k];
}
}

}
``````

poj2752

Step1。将父亲的名字和母亲的名字连接到一个新的字符串S.
Step2。找到一个正确的前缀后缀字符串S（它不仅是前缀，而且是S的后缀）。

``````ababcababababcabab
aaaaa
``````

``````2 4 9 18
1 2 3 4 5``````

``````import java.util.Scanner;

public class poj2752 {

public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(in.hasNext()) {
s = in.next().toCharArray();
n = s.length;
next = new int[n+1];
ans = new int[n+1];
next[0]=-1;
getnext();
int j = n;
int cnt = 0;
while(j!=0) {
ans[cnt++] = j;
j = next[j];
}
for(int i=cnt-1;i>=0;i--)
if(i!=0)
System.out.print(ans[i]+" ");
else
System.out.println(ans[i]);
}
}

static char[] s;
static int[] next;
static int[] ans;
static int n;

static void getnext() {
int j=0,k=-1;
while(j<n) {
if(k==-1 || s[j]==s[k]) {
j++;
k++;
next[j] =k;
} else
k = next[k];
}
}
}
``````

0条评论