首页 > 其他分享 >kmp

kmp

时间:2023-03-24 09:46:03浏览次数:43  
标签:lena int ne while maxn kmp include

 

1.模板题:


给定两个数字序列 a[] 和 b[]b[] 有可能整体作为一个连续子序列出现在了 a[] 中,现在请你找出 b[] 在 a[] 中第一次出现的位置(起始位置从 1 开始计数),如果一次都没有出现,请输出 -1。

输入格式


第一行包含一个数字 T,表示测试用例的个数。
对于每组测试用例,第一行包含两个数字 n m ( 1<= n <= 1000000, 1 <= m <= 10000 ),表示 a[] 和 b[] 的长度。
接下来的一行包括 n 个数字,依次表示 a[1], a[2], a[3] ... a[n]-1000000 <= a[i] <= 1000000 
接下来的一行包括 m 个数字,依次表示 b[1], b[2], b[3] ... b[m]-1000000 <= b[i] <= 1000000

输出格式


对于每组数据输出一行,请输出 b[] 整体作为一个连续子序列在 a[] 中的首次出现位置的起始下标,如果一次都没有完整出现,输出 -1。

样例输入


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

样例解释


对于第一组数据,1 2 1 2 3 [1 2 3 1 3] 2 1 2,首次出现位置的起始下标为 6。
对于第二组数据,b[] 没有出现在 a[] 中,输出 -1。

 

 

#pragma GCC optimize(2)
#include <iostream>
#include <string>
#include <cstdio>
#include <stack>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
typedef long long ll;
ll read(){
    ll x=0; ll f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
using namespace std;
const int maxn=5e6+100;
int s[maxn];
int p[maxn];
int ne[maxn];
int n,m;
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<m){
        if(k==-1||p[j]==p[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int kmp(){
    int i=0,j=0;
    getnext();
    while(i<n){
        if(j==-1|s[i]==p[j]){
            i++;
            j++;
        }
        else{
            j=ne[j];
        }
        if(j==m){
            return i;
        }
    }
    return -1;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i=0;i<n;i++){
            cin>>s[i];
        } 
        for(int i=0;i<m;i++){
            cin>>p[i];
        }
        if(kmp()==-1){
            cout<<-1<<endl;
        }
        else{
            cout<<kmp()-m+1<<endl;
        }
    }
}

 

2.传送门

求模式串在待匹配串中的出现次数。

Input

第一行是一个数字T,表明测试数据组数。之后每组数据都有两行:
第一行为模式串,长度不大于10,000;
第二行为待匹配串,长度不大于1,000,000。(所有字符串只由大写字母组成)

Output

每组数据输出一行结果。

Sample Input

4
ABCD
ABCD
SOS
SOSOSOS
CDCDCDC
CDC
KMP
SOEASY

Sample Output

1
3
0
0
这个题是出现次数例如:

这个是出现了几次例如
aa
aaaa
这个是三次

下一个题和这一个不一样

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
/*
这个是出现了几次例如
aa
aaaa
这个是三次 
*/ 
const int maxn=1e6+100;
char a[maxn];
char b[maxn];
int ne[maxn];
int lena,lenb;
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int kmp(){
    int i=0,j=0;
    int ans=0;
    getnext();
    while(j<lenb){
        if(i==-1||a[i]==b[j]){
            i++;
            j++;
        }
        else{
            i=ne[i];
        }
        if(i==lena){
            //i=ne[i];
            ans++;
        }
    }
    return ans;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%s%s",a,b);
        lena=strlen(a);
        lenb=strlen(b);
        cout<<kmp()<<endl; 
    }
}

 

剪布条

一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢? 

Input输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。 
Output输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。 
Sample Input

abcde a3
aaaaaa  aa
#

Sample Output

0
3
这个题和上一个不一样:

这个就是不能重复
例如:
aa
aaaa 为2
aa
aaaaaa 为3

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
/*
这个就是不能重复
例如:
aa
aaaa  为2
aa
aaaaaa 为3 
*/
char a[maxn];//长串 
char b[maxn];//短串 
int ne[maxn];
int lena,lenb;
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int kmp(){
    int i=0,j=0;
    int ans=0;
    getnext();
    while(j<lenb){
        if(i==-1||a[i]==b[j]){
            i++;
            j++;
            if(i==lena){
                ans++;
                i=0; 
            }
        }
        else{
            i=ne[i];
        }
        
    }
    return ans;
}
int main(){
    while(~scanf("%s",b)){
        if(b[0]=='#'){
            break; 
        }
        scanf("%s",a); 
        lena=strlen(a);
        lenb=strlen(b);
        cout<<kmp()<<endl;    
    } 
} 

 

next数组的应用

1.传送门

WenDavid喜欢周期循环,看到不是周期循环的字符串就很不爽。
现在WenDavid得到一个字符串,可以在字符串末尾添加若干字符,请你帮WenDavid想想,最少添加多少个字符,才能使得字符串变得周期循环(即至少出现两个循环节)。

Input

第一行是一个整数 T ( 0<T<=100 ) 代表测试数据的组数。
之后T行每行一个字符串,由小写字母组成,字符串的长度3<=L<=100000。

Output

每组数据输出一行结果。

Sample Input

3
ppp
pip
machinelearning

Sample Output

0
1
15

循环节长度

int m=len-Next[len];
if(len%m==0)//这个是代表是不是真好够整个循环 cout<<m <<endl; //每个循环节的长度 cout<<len/m<<endl; //循环几次

解析:

m=lena-ne[lena],这个是循环节,
lena%m==0&&lena/m>=2这个是说正好是一个循环,并且循环长度大于2
m-lena%m//是补充几个构成循环

 

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
char a[maxn];
int ne[maxn];
int lena,lenb;
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%s",a);
        lena=strlen(a);
        getnext();
        int ans=0;
        int m=lena-ne[lena];//循环节 
        if(lena%m==0&&lena/m>=2){//至少右一个循环节 
            cout<<0<<endl;
        } 
        else{
            cout<<m-lena%m<<endl;
        }
    }
} 

判断循环节和一个循环

传送门

cgg给你一个字符串s,问在所有的[0, i]区间是否有完整的循环节,若有,输出i并输出循环次数。(嗯哼,就是这么简单的题意)

Input

输入包含多组样例。
每组数据两行,第一行一个整数n(2<=n<=1000000),为字符串的长度,第二行包含一个字符串。
数据以单个0结尾

Output

对于每个测试用例,在一行上输出“ Test case#”和连续的测试用例编号; 
然后,对于长度为i且循环次数K> 1的每个前缀,输出前缀大小i和以单个空格分隔的循环次数K; 前缀大小必须按升序排列。 在每个测试用例之后打印空白行。

Sample Input

3
aaa
12
aabaabaabaab
0

Sample Output

Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4
判断是不是一整个循环
int p=i-ne[i];
if(i%p==0&&ne[i]!=0){
       cout<<i<<" "<<i/p<<endl;
}

 

 

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
char a[maxn];
int ne[maxn];
int lena; 
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int main(){
    int kase=1;
    while(cin>>lena&&lena){
        scanf("%s",a);
        getnext();
        printf("Test case #%d\n",kase++);
        for(int i=1;i<=lena;i++){
            int p=i-ne[i];
            if(i%p==0&&ne[i]!=0){
                cout<<i<<" "<<i/p<<endl;
            }
        }
        cout<<endl; 
    }
} 

输出循环次数:

传送门

给定若干个长度 ≤ 1000000 的字符串,询问每个字符串最多是由多少个相同的子字符串重复连接而成的。如:ababab 则最多有 3 个 ab 连接而成。

输入格式

输入若干行,每行有一个字符串,字符串仅含英语字母。
输入数据以"."结束。

输出格式

对于每组输入数据输出一行,找出每个字符串最多是由多少个相同的子字符串重复连接而成的。

样例输入

abcd
aaaa
ababab
.

样例输出

1
4
3
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
char a[maxn];
int ne[maxn];
int lena; 
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int main(){
    while(scanf("%s",a)){
        if(a[0]=='.'){
            break;
        }
        lena=strlen(a);
        getnext();
        int ans=1;
        int p=lena-ne[lena];
        if(lena%(lena-ne[lena])==0){
            ans=lena/(lena-ne[lena]);
        }
        cout<<ans<<endl;
    }
}

 

传送门

题意:一个字符串,求符合EAEBE形式情况下最大E子串的长度

思路:前缀E和后缀E可以用next数组求出,然后在判断中间的E是否存在,具体做法是:next[len]=i,在[2 * i ,len - i](因为不能重合)内找是否有next[j]=i,存在则i就为答案,不存在的话令i=next[i],而不是i--,继续找
这个题不理解的话可以自己画一下

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
char a[maxn];
int ne[maxn];
int lena; 
void getnext(){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<lena){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else{
            k=ne[k];
        }
    }
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%s",a);
        lena=strlen(a);
        getnext();
        int ans=0;
        for(int i=ne[lena];i;i=ne[i]){//就是缩小公共前后缀
            for(int j=i*2;j<=lena-i;j++){
                if(ne[j]==i){/说明和开头一样
                    ans=i;
                    break;
                }
            }
            if(ans){
                break;
            }
        } 
        cout<<ans<<endl;
    }
} 

 

 

传送门

 

就是找一个k和p,让去掉前k个字母剩下的形成的循环节为p,让求k+p最小的情况下k最小

这个题就是把字符串反转然后再求

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int maxn=1e6+100;
int a[maxn];
int b[maxn];
int ne[maxn];
void getnext(int l){
    int j=0,k=-1;
    ne[0]=-1;
    while(j<l){
        if(k==-1||a[j]==a[k]){
            j++;
            k++;
            ne[j]=k;
        }
        else
            k=ne[k];
    }
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>b[i];
    }
    for(int i=0,j=n-1;i<n;i++,j--){
        a[i]=b[j];
    }
    getnext(n);
    int z=0x3f3f3f3f;
    int ans1=0;
    int ans2=n-ne[n];
    for(int i=1;i<=n;++i){
        int k=n-i;
        int p=i-ne[i]; 
        if(k+p<z){
            z=k+p;
            ans1=k;
            ans2=p;
        }
        else if(k+p==z&&ans2>p){
            ans1=k;
            ans2=p;
        }
    }
    printf("%d %d\n",ans1,ans2);
} 

 

标签:lena,int,ne,while,maxn,kmp,include
From: https://www.cnblogs.com/lipu123/p/14599558.html

相关文章

  • KMP算法
    目录KMP算法前缀函数的定义举例前缀函数的应用KMP算法统计每个前缀的出现次数字符串压缩应用应用1:Leetcode题目分析代码实现KMP算法Knuth-Morris-Pratt字符串查找算法(简......
  • KMP算法
    KMP算法简介一种由Knuth(D.E.Knuth)、Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法。该算法主要解决的就是字符串匹配的问题。本文参考前缀......
  • KMP算法思考
    第一个全匹配没有价值,从第二个开始    采取每次匹配的最大值,则next数组为 计算next数组时也用了KMP算法,因此当不匹配时,j=next[j]; ......
  • 希尔排序、快速排序、KMP算法
    希尔排序背景对N个样本进行排序,顺序为从小至大步骤第一步:对样本不断确定分组的间隔gap,确定分组次数X-》对应第一个循环第一次gap=N/2;第二次gap=gap/2;......
  • KMP
    KMP算法思路有如下情况(这里原串&子串都是从1开始)原串\(s\)(以下简称\(s\))和子串\(p\)(以下简称\(p\))进行匹配,直到黑色分界线时都是匹配的,直到其后面一个元素不相等,\(s[i]......
  • 字符串匹配之KMP算法中的pnext表
    pnext表的分析上篇我们提到了最后是构建一个pnext表,记录着每个字符匹配需要移动的长度的位置信息,接着上篇的内容,我们来分析下pnext表的构造。还是举个栗子:ababcabcacb......
  • 算法训练Day9| LeetCode28. 找出字符串中第一个匹配项的下标(KMP算法)
    28. 找出字符串中第一个匹配项的下标给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从0开始)。如果......
  • KMP算法
    KMP算法是字符串匹配算法,就是从指定字符串里找到匹配串匹配的位置字符串匹配无非是一个个去匹配单个字符,按照通常的思路,我们只需要从头开始一个个往下比就是,但是这样的效......
  • KMP字符串匹配算法——PMT数组的计算
    Leetcode28.找出字符串中第一个匹配项的下标KMP算法和PMT的介绍如何更好地理解和掌握KMP算法?-海纳的回答-知乎KMP算法PMT数组与next数组构造解释KMP算法就是......
  • kmp 与最小循环节
    题目:一个字符串的前缀是从第一个字符开始的连续若干个字符,例如abaab共有5个前缀,分别是a,ab,aba,abaa,abaab。我们希望知道一个N位字符串S的前缀是否具有循环节。......